For many APIs with large numbers of classes, it can be very useful to be able to do a quick once-over to
make sure that classes, methods, and inheritance is correct, before doing more comprehensive testing.
This module aims to provide such a capability.
UsingTest::ClassAPI
Test::ClassAPI is used with a fairly standard looking test script, with the API description contained in
a __DATA__ section at the end of the script.
#!/usr/bin/perl
# Test the API for Foo::Bar
use strict;
use Test::More 'tests' => 123; # Optional
use Test::ClassAPI;
# Load the API to test
use Foo::Bar;
# Execute the tests
Test::ClassAPI->execute;
__DATA__
Foo::Bar::Thing=interface
Foo::Bar::Object=abstract
Foo::Bar::Planet=class
[Foo::Bar::Thing]
foo=method
[Foo::Bar::Object]
bar=method
whatsit=method
[Foo::Bar::Planet]
Foo::Bar::Object=isa
Foo::Bar::Thing=isa
blow_up=method
freeze=method
thaw=method
Looking at the test script, the code itself is fairly simple. We first load Test::More and
Test::ClassAPI. The loading and specification of a test plan is optional, Test::ClassAPI will provide a
plan automatically if needed.
This is followed by a compulsory __DATA__ section, containing the API description. This description is in
provided in the general form of a Windows style .ini file and is structured as follows.
ClassManifest
At the beginning of the file, in the root section of the config file, is a list of entries where the key
represents a class name, and the value is one of either 'class', 'abstract', or 'interface'.
The 'class' entry indicates a fully fledged class. That is, the class is tested to ensure it has been
loaded, and the existance of every method listed in the section ( and its superclasses ) is tested for.
The 'abstract' entry indicates an abstract class, one which is part of our class tree, and needs to
exist, but is never instantiated directly, and thus does not have to itself implement all of the methods
listed for it. Generally, many individual 'class' entries will inherit from an 'abstract', and thus a
method listed in the abstract's section will be tested for in all the subclasses of it.
The 'interface' entry indicates an external interface that is not part of our class tree, but is
inherited from by one or more of our classes, and thus the methods listed in the interface's section are
tested for in all the classes that inherit from it. For example, if a class inherits from, and
implements, the File::Handle interface, a "File::Handle=interface" entry could be added, with the
"[File::Handle]" section listing all the methods in File::Handle that our class tree actually cares
about. No tests, for class or method existance, are done on the interface itself.
ClassSections
Every class listed in the class manifest MUST have an individual section, indicated by "[Class::Name]"
and containing a set of entries where the key is the name of something to test, and the value is the type
of test for it.
The 'isa' test checks inheritance, to make sure that the class the section is for is (by some path) a
sub-class of something else. This does not have to be an immediate sub-class. Any class refered to
(recursively) in a 'isa' test will have its 'method' test entries applied to the class as well.
The 'method' test is a simple method existance test, using "UNIVERSAL::can" to make sure that the method
exists in the class.