|
Helping ordinary people create extraordinary websites! |
Building Perl projects with MakeMakerBy Sean Dague2005-05-01
Building Test Cases (make test) Now that the code is written, it is time to write the test cases. Depending on your style and the complexity of the code, it might have been worthwhile to start with test cases, and then build the code that conforms to those tests. As stated previously, there is more than one way to do it. Test scripts go in the t directory, as stated before. A test script is any program that prints to STDOUT in a defined format. The first line of this output must contain the string 1..N where N is the number of tests that will be run. Each line after that contains either "ok" or "not ok" depending on whether the test succeeded or failed. Test.pm To facilitate building test scripts, there is a Test module included with the Perl core distribution. Test provides a number of functions that automate writing test scripts. The first is the plan function, that is used to generate the 1..N line. If your test script contained five tests, you would have the following code begin your script: Test script header use Test;Why is this easier than print "1..5\n"? If all plan did was generate that line, it wouldn't be. Plan, however, supports a number of additional functions like todo lists, which will be described later. The next function of interest is the ok function. This will generate the appropriate "ok", "not ok" output based on a number of different truth tests. If ok() is called with one argument, it will generate "ok" if that argument evaluates to true in Perl, and "not ok" if it evaluates to false. In the two argument case, ok will generate "ok" if the two arguments are equal as tested by the perl "eq" operator. This tests ASCII equivalence, so 1 will not be equal to 1.0, even though they have the same numeric value. Ok() also has a special case when called with two arguments. The second argument may actually be a Perl regular expression specified via either a string of the form /pattern/ or through use of the qr() operator. In this case ok will generate "ok" if the first argument satisfies the regular expression provided by the second argument. The Test module also provides a few other features. You can specify that certain test cases are on the "todo list". These tests won't generate errors if they fail. If they succeed, they will generate an unexpected success warning. This is generally a good indication that it is time to take the test off the todo list and make it a required test. One specifies todo tests by passing a todo array to the plan function. The following example will set up 10 test cases, the final three of which are on the "todo list" and hence don't have to return "ok" for the overall test script to succeed. Test script header with todo list use Test;One last function provided by the Test module, is the skip() function. There are times when a test case is not appropriate for all platforms or all configurations, but it would be nice to run the test case if it is appropriate. The skip() function takes three arguments. If the first one is true, the test is skipped; otherwise, the second and third arguments are passed to ok just as documented above. Testing Net::Ping::HTTP There is not a great deal of function in Net::Ping::HTTP, so the tests will be rather easy and short. I'll create seven tests for this module. By contrast, version 5.6.1 of the Perl interpreter specifies 12967 test cases, which are run every time the interpreter is compiled. The first test of any module is whether or not it loads at all. For this set of tests, everything will depend on whether or not this first test succeeds. If it doesn't, the code calls croak() (which is a nicer version of die provided by the Carp module), as there is no reason to run the other test cases. In the case of Net::Ping::HTTP, this can be done via the following: Simple load test case eval { require Net::Ping::HTTP; return 1;};
As you write more and more test cases, you will find that eval blocks are very useful for many tests. You then need only test to see if $@ is set to see whether or not you succeeded. In this case if the require failed, $@ would have been set, hence the test failed.
After the simple load test, the object will be tested to ensure it was instantiated properly. This is probably overkill in this particular case, but with more complicated modules, it is definitely a good idea. Here is the set of tests used to check that the object is well formed: Ok test cases my $pinger = new Net::Ping::HTTP();This tests that TIMEOUT is correctly set to the default specified, that the LWP::UserAgent was instantiated correctly, and that the timeout value was propagated to the object as expected. If you are installing this code on a UNIX machine, there are decent odds that apache is running on that box. If it is there, it will most likely be running on port 80. This can be made a skip test to try to ping localhost if apache is in the process list. The code snippet shown will accomplish this task. Skip test case my $count = 0;First, the 'ps -ef' is run to snarf the process list. On a machine where ps is not a valid command, this should return an empty array. Next, the process list is queried for the number of httpd processes within it. Then the pinger pings localhost on port 80, dying if a bad status code is returned. If there are no httpd processes in the process list, the results of the test are discounted. There are some problems with this test case, like the fact that httpd could be running on a high port, or localhost could resolve to something other than the local interface, but for the sake of time and space I will ignore these. Depending on the robustness required by your program, your tests may want to take these possibilities into account. Finally, I will add a couple of tests to ping actual sites on the Internet, www.ibm.com and www.yahoo.com, as both of these sites are highly available. These tests do need network connectivity to those sites without a proxy having been specified. This environment may not exist for many machines. Because of this, I will put these tests on the todo list by modifying my BEGIN block. If they work... great, if not, that's ok too. The new BEGIN block looks as follows: Final test script header BEGIN {plan tests => 7 , todo => [6,7]};The test script for Net::Ping::HTTP is now complete. It has four general tests, one platform-specific test, and two tests marked as todo because they are optional.
After the test script has been added to the MANIFEST, it looks as follows. MANIFESTA word about testing binaries Testing binaries is a bit harder than testing modules, as you are either running them via system() calls and just testing for exit codes, or you are running them with back ticks and then parsing their text output. In either case, it can get ugly fast. That is one of the many reasons why I prefer the paradigm of placing as much core function into modules as possible, and having scripts be little more than small wrappers around these modules. For these reasons I am not going to build a test harness for the pingwww program. Tutorial Pages: » The Module that Makes Makefiles and Much More » Programming Products vs. Programs » Anatomy of a MakeMaker Project » Your First MakeMaker Project (make) » Building Test Cases (make test) » Installation (make install) » Distributing Your Code (make dist) » Conclusion » Resources First published by IBM DeveloperWorks
|
|