Legacy Code

Postmodern VB6: A Quick Start with SimplyVBUnit

In a previous post about the “VB6 problem” I wrote that salvaging business critical classic Visual Basic applications involved these steps:

  1. Build it
  2. Test it
  3. Change it
  4. Repeat

That post (Setting up a VB6 Environment) concerned the first step and will take you through setting up an environment which can build legacy applications using Visual Studio 6. This post starts the discussion about the second step: testing.

I’ll start by repeating a critical thing to remember about legacy code: in its current state, the legacy code supports today’s business.

Consider the case where that statement is not true. Imagine that the legacy application does not support today’s business. If this were true then it would follow that either the business is failing or the application is no longer being used.

Although the application supports today’s business, we often feel that the application could support the business better. An application could be more efficient in terms of computing or human resources by taking less time to run or interact with. We may want to capture new business by adding features. Whatever the changes and our reasons for them, we need to make these changes without breaking the current functionality, and that’s where testing comes in. We can write tests to characterize the application’s current behavior. We assume this behavior is correct because it supports today’s business. Then we make changes and re-run our tests to ensure that we haven’t broken anything in the process.

We must take this characterization step even when we don’t assume that the legacy code is “correct by default”. If we are changing the code to fix a long standing bug, we still want to know how the code behaves today. Once we have characterized the current behavior with a test, we can change the test to expect the desired behavior. Finally, we change the code to correctly express the new behavior.

Before we can apply these concepts to the specific case of salvaging classic Visual Basic code we need to know more about the testing tools available for VB6. In this post we will look at a specific unit testing tool for Visual Basic called SimplyVBUnit. SimplyVBUnit is an MIT licensed open source project, which is actively maintained (the most recent update as of this writing was April 18, 2015–not that long ago at all). There are actually a couple more unit testing frameworks for VB6, but SimplyVBUnit seems like a good way to get our feet wet.

Install

If you followed my first guide to setting up a VB6 environment, then you will want to install SimplyVBUnit inside XP Mode. Use the Start Menu to select Windows Virtual PC and then Windows XP Mode. (If you set up your environment differently, then I will trust you to install it wherever is best for your environment.)

The SimplyVBUnit project provides an installer program, so fire up IE8 and download the latest version from SourceForge. You may need to click a little warning banner to allow the download.

Download SimplyVBUnit
Download SimplyVBUnit

Once the download completes double click the installer to run. Again, you may have to click through a security warning to let XP know you really want to install.

SimplyVBUnit Setup Wizard
SimplyVBUnit Setup Wizard

The setup wizard is easy to use, just click Next until the wizard completes. Unfortunately, the installer is not the end of the story.

SimplyVBUnit has a decent wiki on SourceForge. This wiki includes a page on the installation and a few on setting up tests. The installation page reads like release notes and pretty much just tells us to run the wizard. When we get to the new project instructions, the very first instruction shows a SimplyVBUnit Project template in the VB6 New Project dialog. However, I don’t see that on my system after completing the wizard.

No Template
No Template

To see the template, you will need to take the following additional steps inside XP Mode:

  1. Open My Computer
  2. Navigate to C:\Program Files\SimplyVBUnit 4.1\Source\Projects
  3. Copy all files in this folder.
  4. Paste these files in C:\Program Fies\Microsoft Visual Studio\VB98\Template\Projects
Install Template
Install Template

Now when you start VB6, the template should appear. Close Visual Studio, then log off of XP Mode so that we can start experimenting with SimplyVBUnit.

Create Test Project

Now start VB6 as a virtualized application. Once VB6 starts, you should be able to follow along with the getting started guide on the Wiki. First, create a new SimplyVBUnit Project. Next use the Tools menu to select Options, then select the General tab in the dialog that appears. Change the Error Trapping option to Break on Unhandled Errors. Click Ok.

Now you can use the play button to start debugging. The SimplyVBUnit test runner will launch and you can click Run. Your test run should indicate success, since you currently have no tests, and therefore no test failures.

SimplyVBUnit Test Runner
SimplyVBUnit Test Runner

Create Production Library

Although our overall goal is to learn how to salvage legacy code, our immediate goal is to learn about SimplyVBUnit. So let’s take the easy path and continue to follow along with the wiki by creating a brand new class library to test.

Stop the debugger if it is still running. Next, use the File menu to select Add Project..., then choose ActiveX DLL. By default, this project will be created with the name Project1, and contain a class called Class1. I can’t stand such terrible names, even during a demo, so I will make up some better ones: CoolCalculator for the project and Calculator for the class.

With that very important step completed, we can continue by adding a reference to CoolCalculator in the SimplyVBUnitTesting project. Select SimplyVBUnitTesting in the Project Group pane, and then use the Project menu to select References.... CoolCalculator should be near the top of the Available References list, with its check box unchecked. Check the box, then click Ok.

Add References
Add References

The wiki advises us to save our work so far, and that seems like a good idea. Use the File menu to select Save Project Group. VB6 starts by saving each file, and by default it wants to put the files in the VB98 folder under Program Files. This is no good, navigate to My Documents and create a folder called CalculatorDemo and save your files there. You will need to save the Calculator class, the CoolCalculator project file, the frmTestRunner form from SimplyVBUnit, and the SimplyVBUnitTesting project file–also from the SimplyVBUnit template, and finally you need to save the project group file using the name CalculatorDemo.

Create Test Class

Now we will create a test class and use it to drive a feature in CoolCalculator. Right click on SimplyVBUnitTesting in the project group, and select Add then Class Module. Make sure Class Module is selected in the dialog that opens, and click Open.

Once again, this creates a class called Class, so rename it to CalculatorTests. Next we will register this test class with the test runner frmTestRunner. Right click on frmTestRunner and select View Code.

The frmTestRunner contains a stub for Form_Load with comments that explain how to setup a test case (which we have already started). SimplyVBUnit also provides a comment demonstrating how to register a test case with the runner. Register our test case now by adding the following code to the line below the example comment:

AddTest New CalculatorTests

Now we can add a test to our test suite. Let’s make sure our calculator can add. Type the following code into the CalculatorTests class.

Option Explicit

Public Sub Add_Two_Numbers()
Dim calc As New Calculator

Dim actual As Integer
actual = calc.Add(1, 1)

Assert.That actual, Iz.EqualTo(2), "1 + 1 = 2"
End Sub

This code creates a new Calculator instance, uses it to add 1 to itself, then constructs a test assertion to verify the result. We can hit the play button to see what happens when we try to run this test. We expect that the test runner will try to run the test, since we registered it. We also expect that the test will fail, since Calculator has no implementation.

Test Failure
Test Failure

As expected, the test runs then fails as predicted. If we click Ok on the compilation error dialog, we will drop into the debugger at the beginning of the Add_Two_Numbers method (yellow highlight). Notice also that the .Add method is highlighted in blue.

Having verified our expectations, lets stop the debugger and add some code to the production library. Open the Calculator class and add these lines of code:

Option Explicit

Public Function Add(ByVal left As Integer, ByVal right As Integer) As Integer

End Function

This code should get us past the compilation error, because it will provide the method Add, which is expected by the test. However, we still expect the test to fail, because Add has no body, and therefore we expect it to return the default value for integer, which is 0.

Another Failure
Another Failure

Once again, the test runner confirms our expectations. So lets add a little more code to see if we can get the test to pass. Stop the debugger and add the following line of code to the Add method body.

Add = left + right

Use the play button to run the test again. At this point we expect that the test should pass. The test runner confirms our expectation.

Passing Test
Passing Test

The wiki goes on to explain Testing Multiple Scenarios, which looks to me like data driven or theory tests, depending on which jargon you prefer. That’s a nice feature, but I’ll leave its exploration as an exercise for the reader.

We are done with the CalculatorDemo project for now, so close VB and save any changes.

Review

In this post we used the classic Visual Basic environment that we created in Setting up a VB6 Environment to explore a native Visual Basic testing tool called SimplyVBUnit. Although the SimplyVBUnit installer did not automatically copy the testing templates for us, we were able to work our way past that problem and build an example test which we used to drive the creation of a simple calculator feature.

Although working with SimplyVBUnit was relatively simple, there are two things to consider before choosing it as the means to salvage VB6 code and make it safe to update. First, remember that the example we built for this post did not focus on characterizing legacy code. Instead, we took the easy path of creating a new project so that we could become familiar with SimplyVBUnit mechanics. Before going further, it makes sense to explore characterizing an existing code base with SimplyVBUnit. What do I even mean by characterization, and might there be pitfalls that we have not yet uncovered on the easy path?

Second, we should ask ourselves if native VB6 unit testing is the way to go. As discussed in the first Postmodern VB6 post, our goal when working with VB6 should be to get to a state where we aren’t working with VB6 anymore. If we accept that goal, and plan to migrate to a new platform, then we should seriously consider writing our tests in the new platform. After all, if we succeed in migrating, then all VB6 code will be retired, including the tests we are writing now. Furthermore, all the tests we write in VB6 will just be more code we need to convert to the new platform later. Even if we are able to figure out how to add SimplyVBUnit tests to an existing codebase, then we should still explore the possibility of using a testing tool which is native to our target platform.

Those considerations aside, it’s good to have SimplyVBUnit in our tool set, and I hope this post makes it easier to started.

Legacy Code

Postmodern VB6: Setting up a VB6 Environment

Lately I’ve been talking with folks about the “VB6 problem.” Although some love the platform, and some hate it, the classic flavor of Visual Basic on life support at best, while it’s IDE is in a body bag wondering why no one has taken it to the morgue. There is a petition to bring the platform back to life, or to open source the existing runtime, or even to create a new open source runtime. These ideas offer hope to some, but organizations that rely on VB6 programs for a necessary business process find hope a thin comfort. For these organizations, it is past time to move on. Moving on means rewriting or refactoring, although in this case “salvaging” might be a better term. How to choose a way forward?

Many organizations may get away with taking the inadvisable rewriting route. Some applications are small and well understood, or not actually that critical. However, teams with large, complex and critical applications do exist. Perhaps the application’s author has left the team, or can’t remember the purpose behind all the implementation details. Could you correctly classify a given line of code written six months ago as dead, a bug, a fix, or a critical business rule?

You can? Congratulations, I knew my readers were all above average. Lets try something harder, can you do the same trick for code written in 1998?

(Were you even born when that code was written by a junior developer who is now the CTO and very proud that his code has been running “flawlessly” all these years?)

The one thing we know about production code is that it supports the current business, maybe not perfectly, but it does work. To continue to support the “working” quality of the application will require a refactor/salvage operation. But what challenges lie ahead for those who choose this path with VB6?

Well, that’s what this post is about. If you want to salvage code, you need to build it, test it, change it, and repeat. That’s challenging enough when the code will remain on the same platform, say a .net 2.0 to .net 4.5 migration. But with a VB6 salvage, you have the added challenge of migrating to a new platform.

That is certainly too much ground to cover in one post, so lets look at the first step, building the code.

Setup Visual Studio 6.0 on a modern platform

If you want to build classic Visual Basic projects, you will need a copy of Visual Studio 6.0. This software can be harder to find that you might expect, since Visual Studio 6.0 is no longer available from MSDN. I believe this is because of a settlement with Sun Microsystems which required Microsoft to stop distributing software that included the MSJVM. But I’m not a lawyer, I just know the software is not there. You can still buy copies on ebay and prices range between $200 and $400 (at a glance, as of today, YMMV, etc…).

Lucky for me I acquired a used copy from the SDSU bookstore in late 2001 for $50, and it the 5 CDs (2 for Visual Studio, 2 for MSDN, and 1 for Visual J++) have been living on various closet shelves all these years.

Visual Studio 6.0 Media
CDs, oh yeah….

Of course, these days having CDs doesn’t always mean that you have access to the software.  Many machines, including my main development machine, don’t have optical drives. So what I really wanted was ISOs. My desktop machine has an optical drive and I used a handy little utility called ISO Recorder to copy the data off the disks and into image files.  I was careful to check the back of the CD jewel boxes and sleeves for CD key stickers, and copy the keys into read me files which I stored along side the ISO images.

My current dev machine is a MacBook Pro, and after transferring the ISOs onto the laptop I created a new virtual machine with vmWare Fusion. I did some research to see which version of Windows I should try to install VS6 onto. While some blogs and form posts report successful installs all the way up to Windows 8, I formed the impression that the experience began to degrade with Windows 7. This makes some sense. Although the VB6 runtime is currently supported all the way through Windows 8, the development tools reached end of life in 2008. So, there are caveats if you try to install VS6 onto later operating systems. For example, there is an incompatible component you must de-select during install, or the install will hang. You must run the install as admin, or it will reportedly fail. Finally, some users report that the IDE must run with Windows XP compatibility shims applied. This is all hearsay from me and I’m not even providing links to these reports because I decided to cut to the chase instead of trying every combination of OS and tweaks until one works.

If we can get the IDE to run by pretending to be Windows XP, why not just use Windows XP Mode or an actual Windows XP install? I decided to go with XP Mode because I prefer Windows 7 to XP, and double virtualization is always a blast. I grabbed a Windows 7 ISO and stood up the new VM with the default storage (60GB) and RAM (2GB) provided by the vmWare wizard. After the Windows installation completed, I used boxstarter to install some essential applications and utilities and all critical Windows updates. This can take awhile, but I look at it as a “pay me now or pay me later” choice. Eventually, those updates need to come down, so I choose to get it over with up front.

You might wonder why I’m installing utilities into Windows 7, when Visual Studio 6.0 will run in XP Mode. Remember, this is a salvage operation with the goal of migrating to a modern platform. It could be any platform, but lets just assume we are going to .net. As the salvage progresses, I will use VS6 less, and switch to more modern tools, which will run natively on Windows 7. I will install the minimal number of tools into XP Mode, and my main environment will be Windows 7.  Visual Studio 6 will run alongside these modern tools in a window as a virtualized application.

Visual Studio 6 as Virtualized Application
The Goal

Eventually I will install a modern version of Visual Studio into the Windows 7 host and use it to work with the salvaged code as I port it to .net. For me this is one reason to stick with XP Mode instead of a full XP install. I want to make sure that my modern tools work seamlessly, without putting Visual Studio 6.0 into an uncomfortably modern environment.

Once the boxstarter script finished the initial configuration, I downloaded Virtual PC and Windows XP Mode from Microsoft’s download site. Remember that XP is also at the end of its life, so if you are even considering going this route, grab these installers, set everything up and make a backup. Microsoft is under no obligation to make these tools available, and as we saw with Visual Studio 6.0, outside events like lawsuits can have an impact on whether or not you can get the software in the future. Anyway, enough FUD, installing these two tools are straightforward run-and-click wizards. Be aware that the Virtual PC installer requires a reboot. Also, if your setup is similar to mine, vmWare will prompt you for your admin password whenever you start the machine after XP Mode is enabled. Apparently the way that XP Mode hooks into network traffic, requires your permission. When you are done, you’ll have a shiny copy of Windows 7 with a (shiny?) copy of Windows XP running inside it, waiting for you to install Visual Studio.

Windows 7 with Windows XP Mode
XP Mode Achievement Unlocked

To install Visual Studio 6 you will need to insert the first CD into the virtual optical drive on the Windows 7 host. In vmWare, you click on the little CD icon and select “Choose Disc or Disc Image”.

Optical Drive Callout
Insert disc here

Select the image for the first Visual Studio CD-ROM, then click the CD icon again, and choose “Connect CD/DVD”.” In a moment, Windows 7 will recognize the disk can you should be able to see it in File Explorer. If Windows XP Mode was running when you inserted the disk, then XP will also automatically recognize the disk and you will see it in My Computer. Double click the optical drive in My Computer and setup should launch.

Disc available in XP Mode
Disc, Ahoy!

The wizard will guide you through the rest of the setup. An XP Mode reboot will be required once the installer updates the Microsoft Virtual Machine for Java (Yes, the very same troublemaker that caused the lawsuit). I choose to install “Visual Studio 6.0 Professional Edition” and not the “Server Applications” because I have to make a choice before I can click next. If I must, I’ll come back for the server applications later. I click “Next” and “Continue” until another wizard starts and eventually asks me if I want to customize setup. I choose to customize, and then I choose “Select All” since I have no idea what components might be required during salvage operations. I choose to “Register Environment Variables” when prompted. I am going to opt into everything I can, since this is a virtual machine dedicated to one purpose: make salvage as easy as possible. Finally the wizard stops asking questions and copies files for a while. After it finishes, it wants to reboot XP Mode again.

When XP finishes restarting, a new wizard pops up, wanting to know if I would like to install MSDN. The default is yes, but you may be wondering why I need MSDN installed locally. Isn’t MSDN all on the internet now? Yes, it is, but that is the MSDN of today, and what we need is the MSDN of years gone by. MSDN was different back then, it was not just the giant reference it still is today, but it was the help file too. So, install it–it’s only two CDs after all.

To install, you will need to “Locate Microsoft Developer Network Disk 1”. This means, “Put the Disc I want into the tray, please.” Our tray is still virtual, so follow the same process you used to insert the Visual Studio ISO, except insert the first MSDN ISO this time, then click OK. Click “Continue”, “OK”, “I Agree”, and so forth until the wizard asks you how much MSDN to install. Choose, “Complete” to get all of it. The wizard copies about 14% of MSDN before asking for the next disk. I guess the wizard took up too much space on the first disk. Do as the wizard commands and it will copy the other 86% off the second disk.

Next up, opt-in to install shield and swap back to Visual Studio Disk 1. Why couldn’t this happen before the MSDN install? Don’t ask questions. Do as the wizard commands. Click “Next” and “Yes” a few more times, but you can opt out of viewing the read me file. To install Back Office, you wil need to insert Disk 2. You might need SQL Server 6.5? Exchange 5.0? Lets rethink this opt-in to everything idea. We can always install it later.

Opt out of registering your product and you are done!

Take it for a spin

When setup completes you should be able to start Visual Basic 6.0 and see it running inside the XP Mode VM.

Visual Basic 6.0
Shiny 1998 tech

Once you have seen the IDE start, close VB and then log off the XP Mode shell. Once you are logged out, close the XP Mode window and you will see a message indicating that the machine is going into hibernation. When hibernation completes, use the Windows 7 start menu to find the Visual Studio applications under “Windows Virtual PC” > “Windows XP Mode Applications” > “Microsoft Visual Studio 6.0”. Click Visual Basic 6.0 and a progress bar will appear while XP Mode “prepares” to run the IDE. This just means XP Mode is resuming from hibernation. If you didn’t log off from XP Mode before hibernating, XP Mode will get angry with you and ask if it is ok to log you off. The Visual Basic New Project dialog should appear within thirty seconds or so, along with an alert from XP complaining that you don’t have any anti virus.

On my machine, the IDE text is practically illegible because of the difference between modern monitor resolutions and the resolutions that were common in the Visual Studio 6 era. So I used vmWare to configure the Windows 7 machine to scale its display. I clicked on the settings wrench to the left of the virtual optical disk, then clicked Display and finally unchecked the option “Use full resolution for Retina display”. This immediately kicks in, and windows will want you to log off so it can “refresh” your settings. Once you log back in, VS6 should be significantly more legible.

We have a development environment that we can actually see, but does it work? Before trying to load our legacy app into the IDE to build it, lets take a trip down memory lane and write a simple “Hello World” application as a smoke test.

Select “Standard EXE” from the New Project dialog.

New Project Dialog
Select Standard EXE

Click the Project1 in the Project panel and rename the Project to “HelloWorld”

Renaming the Project
Rename the project

Click Form1 in the project panel and rename to “HelloForm” change the caption to “Hello World”.

Button Control
Make a button

Next select the Button control from the tool box and draw a button on the form. Select the button and rename to GreetCommand. Change the caption to “Greet”

Greet Button
Greet Command

Double click the button to access the code behind. Type the following in the event handler sub:


MsgBox "Greetings from 1998", vbOkOnly, "Greetings"

Now click the play button to start debugging. Click the “Greet” button to get your greeting.

Debug button callout
Play that funky app

Now lets save the project. Use the File menu to select “Save Project As…”. You will see the file system from the point of view of the XP Mode VM, but don’t worry. The important folders like “Documents” are synchronized across both machines. Navigate to “My Documents”, create a folder called HelloWorld, and save your files there. You will see the same folder and files appear in the Windows 7 Documents library.

Now make the project. Use the file menu to select “Make HelloWorld.exe …”. Again, navigate to the folder you created in “My Documents” and click OK. On the Windows 7 side, double click HelloWorld.exe, then click the button to get your greeting.

Hello World
Greetings from 1998

Congratulations

We have managed to setup a development environment for Visual Basic 6.0 salvage operations. We are hosting our tools in a relatively modern operating system: Windows 7. Windows 7 allows us to create a full blown Windows XP system in order to gain seamless compatibility with even older tools, in this case Visual Studio 6.0. Our Visual Studio installation is working well enough that we can write a Hello World application, compile it, and run it in the more modern Windows 7 environment.

If you are planning on salvaging business critical code, you still have a long road ahead of you. It is my hope that this post makes the first step along the path a little easier. Thanks for reading.