In Part 4 of this series I began rewriting some of the embed code from the UpdateDetail procedure in the Invoice app as a reusable, testable class. I also showed how to create a DLL that's compatible with the CTest test runner utility. Now it's time to create a test procedure.
The first test procedure
Click on New Procedure and add a procedure called Test_CreateDetail_SetPriceAndQuantity_VerifyExtended
.
In the Select Procedure Type dialog click on the Defaults tab and choose Test Procedure (Figure 5).
Figure 5. Choosing the test procedure template
You'll notice that the procedure properties have been preloaded with a prototype and parameters (Figure 6). Do not change these values or the test procedure will not work, and may cause a GPF in CTest or your DLL.
Figure 6. Properties for the test procedure
Now, right-click on the procedure and choose Source to bring up the Embeditor (Figure 7).
Figure 7. The generated code
There really are only two embeds you need to be concerned about in the test procedure. One is the data embed, where you declare any data you need for the test. The other is Priority 5000, right before the return statement. That's where you place your test code.
...
Clearly there's a problem because you haven't yet defined the class!
The simplest way to create classes
There are a number of different ways you can declare classes in Clarion; some of them are fraught with danger. For instance, if you create classes that you export from a DLL so they can be used elsewhere, you had better get everything just right or you'll probably be looking at GPFs.
...
You should now be able to compile the DLL.
Setting up CTest
There's one more thing you should do now to make unit testing as painless as possible, and that's to set up CTest in the C7 Tools menu. Choose Tools | Options, and from the list of Options choose Tools again. Click on Add to create an new external tool entry. Set the Command field to point to CTest.exe, and set the Arguments field to ${TargetPath} as in Figure 8.
Figure 8. Setting up CTest as an external tool
With the test DLL as the active window in the IDE, choose Tools | Run unit tests. You should see the CTest window with the test DLL loaded, as in Figure 9.
Figure 9. Loading the test DLL
You'll notice that the procedure name is in all caps - that's how the symbol is exported from the DLL. The procedure name is reformatted after the test is run, based on information returned by the test.
Press the Run All button. You should see a test failure, as in Figure 10.
Figure 10. Test failure
GetExtended just returns zero. Change the class code to store the price and quantity in private variables, as follows:
...
And what do you know - it works!
Figure 11. Two tests pass!
But wait - that result conveniently rounds out to a single decimal place. What happens if you use, say, a value of $11.45 and a discount of 11%? Plug that into your calculator and 3 * 11.45 * .11 = 3.7785. That's not an acceptable currency value. It should round up to 3.78. And in fact the test returns 3.7785, which is a failure.
...
But why don't I just use decimals entirely, instead of reals? Mainly because decimals have to be passed by address and can't otherwise be a return value. I find that a bit clunky. I'm not aware of any problems with passing the data as reals in this scenario, and if there are they should be uncovered by the unit tests ....
More tests
Figure 12 shows the remainder of the tests, which while not yet exhaustive do a reasonable job of covering the bases.
Figure 12. The test suite
You can find the code for the class in the downloadable source.
Implementing the class
Adding the class to the Invoice app is the same as adding it to the test DLL: just add the same two global include statements. And in the UpdateDetail
procedure, add this line in the data section:
...
You'll also have to put the InvoiceDetail.clw file somewhere it can be found. That means either putting it in a directory that's already listed in the redirection file (such as the Clarion libsrc directory) or updating the redirection file. You may want to set up a source directory for just your own classes.
Summary
The original routine contained a hodgepodge of untestable, difficult to reuse code tied directly to a particular database.
...