CleanCode Perl Libraries
Multi-Lingual Library Maintainability
available: Perl not available: Java not available: JavaScript not available: Certified
not available: Testable
not available: Standalone
not available: Diagnostic


Test::Directory - Provides a test harness for running unit and functional tests against object methods or plain functions.


        use Test::Directory;
        processDir("Net", "Gen.*r");
        processDir("Net", undef, 1);
        $errCount = testList("Data::Handy", $vector, getFuncList("Data/"));
        $errCount = testList("Data::Handy", $vector);


Perl5.005, Test, Data::Handy, File::Handy


Default: processDir, testList

Optional: none


This module runs a series of user-defined regression tests on Perl modules, with the tests themselves defined inside each module for ease of maintenance.

This module provides two entry points, depending on how much work you want it to do. The top-level function is processDir. Call processDir with a directory name, and it will examine each module (.pm) in that directory. Any module which you wish to have tested should include a TEST function which just returns a data structure as specified below. For directory 'xyz', the modules in it must conform to Perl standards. That is, a file '' must declare package "xyz::abc". The variable $xyz::abc::VERSION is read and the function c<&xyz::abc::TEST> is invoked. So the directory containing 'xyz' must be in your Perl path (PERL5LIB).

A lower level entry point is the testList function (which is called from processDir). You may call this directly if you don't want to use processDir's directory processing.

Here's an example data structure illustrating the structure's flexibility. The numbers at the left are not part of the structure; they are just for this discussion.

      $testVector = [
       #1#   minutesToHHMM => [ 0 => "05:00", 60 => "01:00", 95 => "01:35" ],
       #2#   hilightInline => [ ["test",10 ] => "== test ==", ["test",11,';'] => ";; test ;;;" ],
       #3#   getNames => [ [ $hashRef ] => "abc, def, ghi", ],
       #4#   HTML::Generator->new() => [ ],
       #5#   emphasis => [ "a message here..." => "<I>a message here...</I>" ],

The list maps function names to function tests. It is an array rather than a hash so that the order may be preserved and multiple instances may be present, but the notation is written like a hash just for clarity.

Referring to the example above:

Line 1 demonstrates a function which takes a single argument.

Line 2 demonstrates a function which takes multiple arguments.

Line 3 demonstrates that, though the function needs only one argument, it must be inside a list reference.

Line 4 provides an object which will be used in subsequent tests; once an object occurs in the list, following tests no longer specify functions, but rather methods of the current object.

Line 5 specifies a method of the currently defined object.

The BNF syntax of the $testVector is:

        $testVector ::= '[' { <testSpec> ',' } ']'
        <testSpec> ::= <object> | <testCaseList>
        <testCaseList> ::= <functionName> '=>' <testData>
        <testData> ::= '[' { <args> '=>' <result> ',' } ']'
        <args> ::= '[' { <arg> ',' } ']' | <string> | <number>
        <result> ::= <string> | <number>
        <arg> ::= <string> | <number> | <reference>


Multiple arguments to a function must be enclosed within a list reference, as shown in args above. A single string or numerical argument may stand alone as the argument list, but any other single argument must be wrapped within a list reference also, whether it is itself a list reference, undef, etc. A common trap, due to the nature of Perl, is expecting an undefined result with an undefined argument, as in undef => undef inside a hash. But that really means "undef" => undef: remember that Perl automatically quotes the left-hand side of the "=>" operator.

The program normally expects to process functions that return a value, so that value may then be compared against an expected value. However, you may insert calls to void functions as well in order to affect an object's state. In this case, use undef as the expected value.

You may test plain functions or object-oriented methods. Insert an object reference in your testVector before the testCaseList that specifies methods on that object. You may also test static (class) methods by supplying a class name (as a string) in your test data instead of an object instance.

This program, by convention, is to test public methods and functions. In the Java version, this is a constraint of the language itself. Perl, of course, imposes no such constraint, so you are free to include any functions you wish in your test vector. The only impact on a run of this program is in the final report of functions you did not test. Any function whose name does not begin with an alphabetic character will not be included in the list of untested functions. Hence, the convention of naming private functions beginning with an underscore will be honored.

When testing functions (as opposed to methods), only ones in the current module may be tested. So if you have a function foo in module A and a function bar in module B, then the test vector in module A can invoke foo() but not bar().Object methods, on the other hand, may be executed against other modules since the object serves as the conduit. (The Java version does not suffer from this constraint since there are on plain functions; everything is a method.)



processDir(dir, filePat, batchMode)

processDir(dir, filePat)


This function tests all .pm files in the specified directory. Any module having a TEST function is processed; that function must return a structure as required by testList. You may optionally test only selected files in the directory by specifying a filePat. Results are collected and written to STDOUT. Errors are printed to STDERR.


dir - string; directory to process

filePat - optional; regular expression to match within directory

batchMode - optional; boolean; flag indicating to prompt for termination on error or not


testList(module, testVector, funcList)

Executes the test suite specified by the supplied testVector on the specified module. See the introduction in this file for syntax of the testVector. Each executed test will be displayed with a pass/fail indication. At the end of the run, testList displays a summary including how many tests were executed and how many failed. The test report includes the module name and the module version if the module has defined a VERSION variable. The funcList is an array reference containing the names of all functions in the module. As the testVector is processed, the functions are checked off; any untested ones are reported at the end.


module - string; name of module to test

testVector - array reference of test data

funcList - array reference of function names in module


Count of errors from executing the testVector.



Reads the specified file and returns a reference to an array of names of functions contained within. Candidate functions are defined as "sub identifier", where the identifier must begin with a letter (so, for example, names beginning with an underscore, typically private, are not returned). Also the special names "TEST" (used by this module) and "new" (technically a constructor, not a function) are not returned.


filename - string; name of file to process


array reference of function names


Not designed to handle returned objects yet.


Michael Sorens


$Revision: 230 $ $Date: 2008-03-08 18:17:55 -0800 (Sat, 08 Mar 2008) $


CleanCode 0.9




Hey! The above document had some coding errors, which are explained below:

Around line 443:

=back doesn't take any parameters, but you said =back -- end of FUNCTION section

CleanCode Perl Libraries Copyright © 2001-2013 Michael Sorens - Revised 2013.06.30 Get CleanCode at Fast, secure and Free Open Source software downloads