Wednesday, May 10, 2006

FoxUnit is Cool!

FoxUnit has been available for at least of couple of years, and I've always meant to work with it, but it was one of those things I just didn't get around to. However, after attending Nancy Folsom's session on refactoring at the recent GLGDW, in which she discusses the importance of testing both before and after refactoring to ensure the functionality remains the same, I figured it was time to get to it.

What is FoxUnit? As defined on the FoxUnit Web site, 'FoxUnit is an open-source unit testing framework for Microsoft Visual FoxPro®. It is based on unit testing frameworks as described in Kent Beck's book "Test Driven Development by Example" but takes a more pragmatic approach to unit testing for Visual FoxPro than a more purist xUnit implementation would.'

The idea is to create tests for the various pieces of your application. You then run some or all of the tests prior to releasing a new build (or performing refactoring or checking in the latest update or whenever you want) to ensure everything works correctly. Note that unit testing isn't a replacement for system or acceptance testing, but is one more tool in your professional developer's toolkit.

What got my current interest in FoxUnit started is the need to refactor some code. One method is particular is huge (several hundred lines long) and has been getting more convoluted with time. Before I add some new functionality, I want to refactor it so it's easier to comprehend, easier to maintain, and easier to test. However, I'm scared that during refactoring, I'll drop some functionality or introduce bugs. Hence the need for suite of tests I can run before and after each refactoring task. (Nancy stressed doing refactoring in small steps rather than one huge job. In addition to being easier to do, it's easier to test and less likely to cause broken functionality).

So, I downloaded and installed FoxUnit. My introduction was a little rough -- because I didn't follow instructions and SET PATH to the folder when I installed it, a few things didn't work right. In my opinion, a app should be able to find its own pieces without having to use a crutch like SET PATH, so I made a few minor tweaks (like having the program figure out what directory it's running in and using that path in a few places rather than assuming the files can be found without one). However, once I got past that, it was pretty easy to work with.

Tests are stored in PRGs which are managed by the FoxUnit UI. Each PRG contains a class subclassed from FXUTestCase, the base class FoxUnit test class. Each test is a method of the subclass (although there can be non-test methods too, of course). Although you could write all of the tests for your entire application into a single PRG, that wouldn't be very granular. I prefer one PRG for a single "thing" (module, class, or whatever) I want to test, and then one or more test methods within the PRG to test the functionality of that "thing".

The idea is to write small tests that each test one aspect of one method. For example, if a method accepts a couple of parameters and does different things based on the parameters passed, there should be tests for each parameter being passed or not, different types of bad values for the parameters, different types of good values that result in different behavior, etc. As a result, you'll have a lot of tests for even the simplest application, but each test is small, easy to understand, easy to maintain, and does just one thing. And since the FoxUnit UI manages all of the tests for you, and gives you options to run a single test, all tests in one PRG/class, or all tests, the management of these tests isn't too bad.

Tests are easy to write. Since it's just code in a VFP class, you can add custom properties if necessary. For example, rather than instantiating the object to be tested in every test method, you could create a property of the test class such as oObjectToTest, and in the Setup method (called just before any test is run), instantiate the object into that property: Here's an example, along with a test to ensure the object actually instantiated properly:

define class MyTest as FxuTestCase of FxuTestCase.prg
oObjectToTest = .NULL.

function Setup
This.oObjectToTest = newobject('MyClass', 'MyLibrary.vcx')
endfunc

function TestObjectWasInstantiated
This.AssertNotNull('The object was not instantiated.", This.oObjectToTest)
endfunc
enddefine

Note the use of one of the test methods, AssertNotNull. There are several similar methods available, including AssertEquals and AssertTrue. These methods test that some condition is the way it's expected to be, and if it isn't, the test fails and displays the failure message specified in the assert call.

In addition to instantiating the object, Setup can be used to perform other tasks needed for every test, such as setting up the environment or the object under test. A similar method, TearDown, can be used to perform common tasks after a test has been run, such as restoring the environment.

When you run a test, it either passes, in which case it's shown in green in the FoxUnit UI, or fails, which is shown in red. Tests that haven't been run are shown in gray. Thus it's easy to visually see the results of test runs.

FoxUnit is one of those things that seems like a good idea until you try it, and once you do, you realize it's a great idea. I'm kicking myself for not trying it out a couple of years ago when I first saw Drew Speedie demonstrate it at DevTeach in Montreal. But now that I've worked with it for a while, I'm a firm believer. So, if you haven't started using FoxUnit, do yourself a favor: take out an hour, download it, and create some simple tests. You'll become a believer too.

3 comments:

Anonymous said...

Writing tests is not about writing optimized, fully normalized code. *g*

Instantiating the object that is tested is part of the test itself, not the preparation to get a test run. SetUp and TearDown should be as generic as possible and contain not more than you would put into the main program. That is: setting paths, loading procedures and libraries, and the like.

As a developer it's hard to write duplicated code, but there are good reasons to avoid putting test code in setup/teardown. For instance, they are run separately. If you want to debug them, you have to step into them, then go back into FoxUnit code, then step into the actual test. Moreover, the setup code at the top is not clearly visible when you look at the test code at the bottom. It becomes difficult to say what you are actually testing. One more *g*: The setup code must be generic enough. What if you want to test if releasing the variable does fire the Destroy event? What if you want to test multiple instances? Both times having the object in a property defeats the test.

Doug Hennig said...

Great comments, Christof -- thanks!

Doug Hennig said...

So far, so good, Eric. I hear ya about coming across new things that get you all fired up, only to leave them behind later on. I've started planning a large set of test cases for Stonefield Query; just haven't written them yet.