WARNING: XUnit has security implications. Please carefully read the security section (below) on the security implications of using the XUnit
test framework.
License
Unlike the other NetKernel modules, which are distributed under the 1060 Public License, the XUnit suite is licensed under a
non-commercial use license. You are free to evaluate xunit and
free to use it for non-commercial use. XUnit is not restricted in any way
and includes full documentation - rather than restrict the product, we prefer to trust you and hope that you will respect that trust by purchasing
a license. Please visit 1060 Research for a license if you intend to use XUnit for
development of commercial applications.
Unit Testing
Unit testing is a valuable part of the development process. XML applications and services
are no different and multiple small test cases help illustrate a service's interfaces,
ensure robustness and provide quality assurance. Unit testing is valuable to ensure
long term maintainability of an application or service.
XUnit
XUnit is an application for executing arbitrary tests on the NetKernel Standard Edition platform. It provides an extensible framework
and test harness for executing groups of unit tests. It provides both XML output and GUI (HTML) visualisation of test results.
The XUnit test engine can be treated as a black-box which is accessed through the xunit accessor. The xunit engine
will recursively execute a test list which has the following form...
<testlist iterate="1" output="html" title="My Tests">
<desc>
<div>Optional XHTML description of this test</div>
</desc>
.
Multiple Individual Tests...
<test>
<uri>active:dpml+operand@ffcpl:/some/test.idoc</uri>
<assert>
..Assertions can be one or more of the following..
<xpath>An xpath to assert on the result</xpath>
<uri>URI to an assertion service</uri>
...
</assert>
</test>
.
And/Or Multiple Groups..
<group title="My Test Group">
<uri>URI to a secondary test list</uri>
</group>
</testlist>
TestList Structure
The RelaxNG schema for the test list is available here.
A testlist consists of one or more tests or groups of tests. The testlist has root element <testlist>
TestList Attributes
- @title - the name of the test
- @output - indicate whether the results should be HTML formatted (html) or XML (xml)
TestList components
- <desc> - An optional block of XHTML description for the tests.
- <test> - A single test, consisting of.
- <uri> - the URI to be invoked as the test.
- <assert> - optional assertion tests on the result (see below)
- <module> - optional uri of module against which public URI address space the test URI will be invoked
- <moduleVersion> - optional version number of module
- <group> - A test group to recursively invoked [may have optional @title attribute]
- <uri> - the URI of the testlist to be recursed
- <module> - optional uri of module against which public URI address space the test URI will be invoked [may be overridden by invidual tests].
If not specified the test URI will be issued into the current URI address space (generally the typical mode of operation).
- <moduleVersion> - optional version number of module [may be overridden by individual tests]
Assertion Tests
When a test URI request has completed the XUnit engine will test the result against any/all specified assertions. Assertions may be of the following types
- <xpath> - An XPath expression
- <uri> - The URI of an assertion service which the XUnit engine will invoke with the test result as the
param argument of the request. The assertion service must return a boolean result (either canonical boolean document or an IAspectBoolean).
- <minTime> - minimum time the test should take (milliseconds)
- <maxTime> - maximum time the test should take (milliseconds)
- <mimetype> - mimetype of result
- <expired/> - result should be expired
- <intermediate/> - result should be intermediate
- <contextSensitive/> - result should be context sensitive, ie the result may not be same if called from a different context (super stack)
URI Requests
XUnit is really just a sophisticated mapper which invokes sub-requests and tests the results. By default, the test URI will be issued as a request to the internal URI
address space from wherever the xunit accessor is invoked. It is recommended that all test URIs should be absolute URIs - no guarantees are made which current working
URI will be used to resolve relative URIs.
Using a Module URI
If an optional <module> URI is specified for a test, the test URI will be requested against the public URI interface of that module. If the testlist document
specifies a module URI then all tests will be requested against the public URI interface of that module. An individual test may specify it's own module URI which will override
the testlist module URI.
Example URIs
The URI of a test may be any NetKernel resolvable URI, here are some examples
- To test a resource exists:
ffcpl:/path/to/my/resource.xml
- To start a DPML test harness use:
active:dpml+operand@ffcpl:/path/to/my/process.idoc
Test Order and Execution State
The test engine will execute all tests specified in a testlist, including recursively included tests from groups. The tests are not guaranteed to be executed
in order - no assumptions should be made about the order of execution of a series of tests. If necessary each test should be written to create any necessary state
for the test - you must not assume address space state will be preserved from another test higher up the testlist.
Creating a Test Harness for XUnit
The XHTML formatted result of the xunit accessor is written to provide an interactive test application. All href links in the result
are relative to the root path ie they come back to the same page with different parameters. Below is a test harness DPML process which
will act as front-end to the XUnit engine - you should set the initial test list URI which will act as the starting point from which
unit tests can be recursively explored.
[Note: If you don't want a GUI interactive application you can specify XML output in the testlist - this will produce an XML list of tests and groups with
their test result status.]
<idoc>
<comment>
*******************************
Example XUnit Test Harness
*******************************
</comment>
<seq>
<instr>
<type>copy</type>
<operand>this:param</operand>
<target>var:param</target>
</instr>
<exception>
<comment>
*********We received no param so we must boot the system***********
</comment>
<instr>
<type>copy</type>
<operand>
<nvp>
<uri>...IMPORTANT: This should be the URI of your highest level TestList...</uri>
</nvp>
</operand>
<target>var:param</target>
</instr>
</exception>
<comment>
***********Execute the Tests**************
</comment>
<instr>
<type>xunit</type>
<param>var:param</param>
<target>this:response</target>
</instr>
<instr>
<type>expire</type>
<operand>this:response</operand>
<target>this:response</target>
</instr>
</seq>
</idoc>
Security
XUnit has security implications!. XUnit necessarily must be able to execute any test URI's, equally
it is capable of serving any resource from the address space in which it is deployed. It is essential that access
to your front-end test harness be controlled - take great care how you export your application's test harness.
For guaranteed security do not deploy your test harness with your application. However with some basic precautions
there is no reason that a deployed application and test suite cannot safely co-exist. The easiest way to achieve this
is to put your unit test harness in an isolated fulcrum with a dedicated access controlled transport
which is used only for testing.
XUnit Self Test
To see XUnit in action try the XUnit self test suite. Simply import the urn:org:ten60:netkernel:ext:xunit module into a fulcrum with an HTTP transport
and point your browser at /org/ten60/netkernel/xunit/selftest.