In the summer of 2004, Vision Data Solutions (http://www.visionds.com)
announced an open-source project called FoxUnit (http://www.foxunit.org), based
on the Kent Beck book, "Test Driven
Development by Example".
As an open-source project, it's supported not only by Vision
Data Solutions developers, but also by other developers using it with full
source code. It requires VFP 8 or newer. The Vision Data Solutions team is also
receiving and incorporating input from other developers as to how to make FoxUnit
more useful for everyone. The documentation, however, is a bit scarce, so it
can be tricky to get started.
FoxUnit deals with exclusively with unit tests. Other types
of testing such as Integration and acceptance require more automated tools.
Test driven development however stresses the importance of unit testing, the purpose
to test each class completely so you can feel confident it performs its purpose
properly. This is supposed to improve code readability and testability. Unit
testing also improves the overall quality of the code, eliminating problems
early in the development cycle, saving time and effort down the road. There are
a few basic tenets of TDD unit testing:
FoxUnit is designed to help you accomplish #3, which is the
most difficult part. TDD is ideally suited for mid-level components because
each class you test has to be able to stand on its own. This means your code
should already be using classes (either .PRG- or .VCX-based) to handle the
dirty work, letting the user interface simply make calls to these classes. If
your visual classes (such as combo boxes and forms) have their own custom
methods, you can write tests for them, but realize you must do more work for
integration testing (when you make sure they all work together).
FoxUnit runs within your current development environment, taking
up less than 1MB. You can place it in its own folder (and add it to your path)
or unzip it right into the main project folder. In your current project folder,
create a new folder called Tests. This is where your test cases will be stored.
Run FXU.PRG, and the list of current tests will appear (figure 1). If this is
the first time you've run FoxUnit, you are prompted to specify or create a new folder
for your tests.

My list of tests – You can filter the list by entering classes
or names in the text boxes.
FoxUnit is a top-level form that shows on the Windows
taskbar. It's non-modal so you can continue to do other work while it loads.
When it isn't visible, FoxUnit adds a menu bar to the system menu that
reactivates it when you need it.
Click on the New Class button to create a new test class.
You can base the test class on templates included with FoxUnit or your own.
After you use a custom template, FoxUnit adds it to the list of available
templates. FoxUnit provides a Standard and Minimal
test case (see below for how to create your own templates).
The Standard test class includes comments and examples of
how you might use the default methods (the minimal one only includes the method
definitions.) There are two built-in methods to the template: Setup and
TearDown. Each new test is added as a new method to the test case. Add common
code you use for each test -- such as setting up paths or instantiating objects
-- into the Setup method. Add cleanup code to the TearDown method. FoxUnit
calls these two methods each time a test runs. For example, I'll test a custom
class called cCustomerClass. This class is responsible for creating and
managing customers. Since the entire test class uses this object for its test
purposes, I've added a property before any methods that points to this object:
DEFINE CLASS CustomerTestClass as FxuTestCase OF
FxuTestCase.prg
oObject
= .NULL.
Now I set the oObject property to the cCustomerClass in the
Setup method:
Function Setup
oObject =
CREATEOBJECT("cCustomerClass")
Now each time a test runs, it fires this method, creating
the object.
There are three other methods hidden in the test class:
AssertEquals, AssertTrue, and AssertNotNull. Use these methods to validate your
results. Let's start with AssertNotNull.
Function WasObjectCreated
THIS.AssertNotNull("Object
was not created",THIS.oObject)
Close the test class program. Your test is now displayed as one
of the available tests. Highlight the test in FoxUnit and click on the Selected button to run the test. If the test doesn't run
successfully, it will appear in red (figure 2).

An Unsuccessful run -- The error/exception message provides
full details of where the code failed to run.
Why did it fail?
Because I didn't tell it where to find the cCustomerClass
object. Here's the fix:
Function Setup
SET CLASSLIB TO ccustclass
oObject =
CREATEOBJECT("cCustomerClass")
When a test runs successfully, it appears in green. Now you
can add further test methods to test the class.
If there are errors in the Setup or TearDown code, they'll
show up in the FoxUnit Errors dialog. If you want FoxPro to
run this code outside the tester (so you can debug it immediately), right-click
and choose Options. There's an option to exclude these two methods.
So what should your customer class do? One thing it should
do is open the Customer table. So, create a test for it.
Let's say the method you call to open a table is OpenFile,
and it returns true or false depending on whether it was successful. My test
method, in this case, might look like this:
THIS.AssertTrue("Table was not
opened",THIS.oObject.OpenFile() )
Each test case doesn't have to be a single line. An initial
test method might define some default variables to work with or retrieve data
from data sources:
Function AddCustomer
lcCust = "AKSEL Solutions"
lcID = THIS.oObject.GetCustID( )
THIS.AssertTrue("Customer could not be
added",THIS.oObject.Add(lcID, lcCust))
AssertEquals can be helpful for evaluating non-logical and
NULL-based expressions. If the Customer Update method is supposed to return 1
if it's successful, the following code tests the result:
THIS.AssertEquals("Customer was not
updated", ;
1, ;
THIS.oObject.Update())
There's another method called MessageOut().
This adds messages to the FoxUnit Message tab. This is great for providing
feedback on the test when you don't want to call the Assert methods.
Function GetCustomer
IF THIS.oObject.GetCustomer("AKSEL")
THIS.messageOut(
"Customer name is
"+THIS.oObject.CustName)
ENDIF
If the GetCustomer test was successful the message should
appear on the Message tab.
The first three buttons on the toolbar run the tests. You
can choose to run all the listed tests, only the ones for the currently
highlighted class, or just the selected one. Each test is logged and run on its.
When you select a failed test, the details of the failure appear in the
Failures and Errors section.
FoxUnit also keeps track of the tests after you exit Visual
FoxPro so when you come back to your development environment later, you can see
which classes you need to fix before running them again. So if you have a suite
of 100 tests, run them all, go for lunch and then come back!
A FoxUnit template is a .TXT file that looks like a PRG
class, with one exception: It uses VFP's TextMerge function to insert the
appropriate class names. Open the fxutestcasetemplate.txt file and look at the
first few lines:
DEFINE
CLASS <<testclass>> as FxuTestCase OF FxuTestCase.prg
#IF .f.
LOCAL THIS AS <<testclass>> OF
<<testclass>>.PRG
#ENDIF
Testclass is the
name the user specifies when creating the new test. Copy your existing test
class with a .TXT extension and change the DEFINE CLASS statement to match this
one. You now have your own reusable FoxUnit template.
Click on the Load Class button to load a test you previously
removed or created in another directory. If you no longer require a particular
test, click on Remove Selected. This removes it from the list of tests, but it
doesn't delete the file.
FoxUnit stores the test classes as .PRG classes in the Tests
folder. You can modify these files outside FoxUnit if you want. Click on the
Reload Selected button to refresh FoxUnit's list of available test methods.
Vision Data Solutions has a webinar with Jim Erwin available
on test-driven development with FoxUnit. It identifies several keys to success:
·
Take small steps when building tests.
·
Keep it simple.
·
Don't tolerate broken tests.
·
Use descriptive names.
·
Refactor your code.
·
Rely on your tests, not your comments.
·
Don't be afraid to start over.
·
Only write for the requirements.
Although setting up the initial tests may take some time,
FoxUnit represents a huge improvement in creating, managing, and running tests.
A big thanks to the Vision Data Solutions team for bringing
this tool to the FoxPro community. Well done!