Randy J Ray


Test::AgainstSchema - Umbrella for test classes that target schema-based data


    use Test::AgainstSchema XML => tests => 5;

    our $schema = "structure.xsd";

    for (qw(file1.xml file2.xml file3.xml reference.xml minimal.xml))
        is_valid_against_xmlschema($schema, $_, "$_ validation");


The Test::AgainstSchema module provides unit tests of the Test::More variety, that work with any TAP-driven test-harness system. The tests are oriented towards testing textual data that follows defined formats, such as XML or YAML. Rather than using regular expressions or string-equality comparisons, the classes provided with this distribution use existing validators. For example, the XML tests (see Test::AgainstSchema::XML) use the XML::LibXML module from CPAN which provides validation for XML Schema, RelaxNG and DTDs (SGML or XML style DTDs).

The tests accessible through Test::AgainstSchema are broken into groups called specializations, each represented by a class that inherits from the Test::Builder::Module class. Each of these can operate as a stand-alone test module, as their inheritance from Test::Builder::Module also provides access to the basic testing functionality (plan, skip, etc.). This module, Test::AgainstSchema, acts as an umbrella that makes it easier to load several of these groups at once.

This class does not actually provide any functionality of its own, except for an import method that removes arguments that identify specializations, loads them into the namespace of the caller, and passes the remaining arguments on to Test::Builder::Module which registers them with the current test-session being set up.

Defining Specialization Classes

A specialization class can stand alone, and does not need to be loaded through the Test::AgainstSchema container. It should be a subclass of Test::Builder::Module, or if it isn't it should provide the full functionality of Test::Builder through its own means.

If a specialization class is loaded through the Test::AgainstSchema umbrella, it will not receive any arguments in the use command that loads it. Any test-suite control arguments are passed on to the superclass. Therefore, the specialization class should not override the import method it inherits from Test::Builder::Module unless the local version ends with a call to SUPER::import(@_) (passing @_ ensures that the class does work when loaded directly, by passing any arguments it received to the parent class). The specialization class should instead rely only on the @EXPORT list to define the functions it provides.

See Test::AgainstSchema::XML for an example of a specialization class.

Loading Specializations from Test::AgainstSchema

One or more specialization classes can be loaded through a single call to Test::AgainstSchema by passing their names in the import-list to the load of this class. As an example:

    use Test::AgainstSchema XML => tests => 5;

This invocation loads the Test::AgainstSchema::XML specialization class, then passes the arguments tests and 5 on to the construction of the test suite.

Such classes do not need to be named Test::AgainstSchema::Something. The Test::AgainstSchema import method scans the list of arguments for any value that is not a reference and whose first character is an upper-case letter. Anything else is presumed to be intended for Test::Builder. If the argument string does not start with Test::, then the value of __PACKAGE__ is prepended to it. Thus, Test::AgainstSchema can itself be used as a base class in which the derived class' name is used in creating the full class name. If the argument's first six characters are Test::, then it is used without modification. The final name, modified or not, is then used in an eval block that tries to load the module via use, in the namespace that initally loaded Test::AgainstSchema (or the class derived from it).

To illustrate the modification of arguments to class names, consider this table:

    Main class name         Argument text  Name of class that gets loaded
    ===============         =============  ==============================
    Test::AgainstSchema     XML            Test::AgainstSchema::XML
    Test::AgainstSchema     XML::Simple    Test::AgainstSchema::XML::Simple
    Test::AgainstSchema     Test::Hooks    Test::Hooks
    My::Test::AgainstSchema MyFormat       My::Test::AgainstSchema::MyFormat

Note that in the third case, no change is made to the argument. And in the fourth case (assuming that My::Test::AgainstSchema is a subclass of Test::AgainstSchema) the modified argument has the derived class prepended to it, not Test::AgainstSchema.


Test::AgainstSchema does not export any subroutines of its own. It only facilitates the loading of specialization classes and the export of their functionality into the namespace of the package that uses Test::AgainstSchema. However, since it is a sub-class of Test::Builder::Module, it is fully usable as a test framework (provided at least one specialization is loaded) and provides all the functionality described in Test::More (in addition to any functions provided by specialization classes). See Test::More for documentation on those functions provided.


See Test::More for a description of the diagnostics produced by the functions provided by it. See the specialization classes for details of their diagnostics.


Please report any bugs or feature requests to bug-test-againstschema at rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Test-AgainstSchema. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.



The original idea for this stemmed from a blog post on http://use.perl.org by Curtis "Ovid" Poe (<ovid at cpan.org>. He proferred some sample code based on recent work he'd done, that validated against a RelaxNG schema. I generalized it for all the validation types that XML::LibXML offers, and expanded the idea to cover more general cases of structured, formatted text.


Copyright (c) 2011 Randy J. Ray, all rights reserved.

This module and the code within are released under the terms of the Artistic License 2.0 (http://www.opensource.org/licenses/artistic-license-2.0.php). This code may be redistributed under either the Artistic License or the GNU Lesser General Public License (LGPL) version 2.1 (http://www.opensource.org/licenses/lgpl-license.php).


Test::AgainstSchema::XML, Test::XML


Randy J. Ray, <rjray at blackperl.com>