CoUnit
Introduction
CoUnit is a unit testing framework for Cocoon applications.
It can be used to specify and execute unit tests for pipelines and stylesheets.
CoUnit is not meant for testing components written in Java; we have JUnit for that. It will also not help to test the we/-frontend of your application; HttpUnit does a fine job in that realm, and so does AntEater. CoUnit helps you to test the bits in between: All the code that lives in the pipelines, transforming XML (SAX events). At the moment, it is particularly good at testing XSLT stylesheets, but Flowscript is not yet covered.
CoUnit can be used with Cocoon 2.1.6 and higher. If you need to use an earlier version of Cocoon, you will need to retrofit the MountTableMatcher from the Cocoon repository after 11 October 2004.
Unit testing and test-driven development
Unit (and regression) testing has been one of the best practices in software development for a long time (I first came acress it in an article in Dr. Dobbs Journal , February 1997). More recently it has become popular as part of the eXtreme Programming method, where it has evolved into test-driven development. There are many good books and web sites about unit testing, which you should have a look at if you have never heard about these methods:
- JUnit - dedicated to software developers using JUnit or one of the other XUnit testing frameworks.
- TestDriven - promoting techniques, tools, and general good will in the test-driven community.
- Test Driven Development - a page at objectmentor.com
Unit testing is about testing your code at a fine level of granularity; a
single method or function. Regression testing extends this method by making a
set of unit tests repeatable and automated, thus giving you an instant picture
of the quality of your code.
Test-driven development takes this idea a step further, and demands that you do
not write tests to check your code after it has been written, but write
them before you write code, as part of the specification of what that
code is supposed to do. The development cycle now becomes:
- Write a test that specifies a tiny bit of functionality.
- Ensure the test fails. (You haven't built the functionality yet!)
- Write only the code necessary to make the test pass.
- Refactor the code, ensuring that it has the simplest design possible for the functionality implemented until now.
On a project, this should be done following these rules:
- After every change, run all tests.
- Refactor the code if a test fails.
- Only check-in the code if all tests succeed.
- When a bug is found, first write a test for it, then refactor the code.
- Tests are organized in ‘suites’ – groups of tests for a particular bit of
functionality.
There are many benefits to this approach, please see the references cited above.
Testing frameworks
Unit testing, and test-driven development, must be easy to do and take as
little time as possible. If not, software developers will avoid it, because "it
is a waste of time", and their managers won't allow it, because "it is a waste
of time". And even if you have experienced the time savings (especially in
non-trivial projects), it is nice to be able to do something useful with as
little effort as possible, so you can concentrate on those things that are more
difficult and time-comsuming than they should be.
Unit-testing frameworks have been developed, which facilitate writing
unit tests, run test suites, and generate reports. One of the most well-known is
JUnit, from which many other XUnit
frameworks sprang. Each of these frameworks targets a specific language or
development environment.
For the purpose of testing Cocoon applications, the following are interesting:
- JUnit: Java
- xUnit: Many other programming languages.
- HttpUnit: Web applications.
- Anteater: Web services.
- XMLUnit: XML; can test equality of XML documents, XSLT results, XPath expressions. Tests are written in Java or C#.
-
XSLTUnit: XSLT style sheet.
None of these does exactly what you need to test Cocoon pipelines, which is
why I decided to build a specific unit-testing framework for this purpose. After
a few iterations, this became CoUnit, which was first presented at the Cocoon
GetTogether 2004.
CoUnit is based on XSLTUnit, which is a single stylesheet developed by Eric van
der Vlist of Dyomeda. XSLTUnit performs a single unit test on a XSLT style sheet
by transforming a XML document, and comparing the result to a 'reference'
output. Unfortunately, it falls short of being a framework, because it cannot
execute test suites, and it doesn't do much reporting. These blind spots are
filled in by CoUnit.
XSLTUnit needs the exsl:node-set() extension function, which is present in both Xalan-J and Saxon.
Requirements
Before starting to create code for CoUnit, I should have defined unit tests for it, but I didn't because I had no unit-testing framework to run them. What I did do, was to make a list of requirements which should ensure that CoUnit is a nice tool to work with:
- CoUnit must be transparent to the applications it tests.
You should be able to set up your Cocoon application any way you like. No assumptions shall be made about sitemap-mounting structures, directories, symbolic links, etcetera.
The only aberration from tis rule is, that you must create a directory called '_test' where your sitemap is. - It must be easy to add tests and test suites.
Code that is not directly related to these must be minimized. - It must be easy to run tests and generate reports.
A request to run all tests can be made via a single URL. - CoUnit may be used in data-processing Cocoon applications.
This was the kind of application that CoUnit was originally developed for. Later, presentation and Flowscripts may be considered.
Using CoUnit to test a Cocoon-based application
Downloading and installing
CoUnit can be downloaded from this page; look for the section "Project resources".
Installing CoUnit is simple: Just unpack it at a convenient place, like in a
sub-directory of the directory where you put all your Cocoon projects.
For example, if your Cocoon projects are in /home/cocoon-projects, create a
sub-directory called CoUnit, and unpack in that directory.
CoUnit uses a mount-table
CoUnit is typically mounted from the main sitemap, or from the main mount-table.
from the main sitemap:
<map:match pattern="test/**">
<map:mount check-reload="yes" src="/path/to/unit-test/framework/"
uri-prefix="test"/>
</map:match>
from mount-table.xml:
<mount uri-prefix="test" src="/path/to/unit-test/framework/"/>
If you plan to run unit tests for the framework itself (which I am busy adding!), use the mount-table, because all testable components need to be mounted that way.
Project resources
- Mailing lists: http://lists.cocoondev.org/mailman/listinfo
-
SVN:
http://svn.cocoondev.org/repos/counit
Frequently asked questions
Q: I always get a NullPointerException trying to run unit tests, even those
that come with CoUnit.
A: You need to update Cocoon to version 2.1.6 or higher, or at least update the
MountTableMatcher to a version later than 11 October 2004. On that date, Sylvain
Wallez fixed
bug
#31637.

