Hi,

I'd like to get your comments on the approach to Unit testing I have used in
developing my Cocoon components. I think that this approach can be extended
to provide unit tests for all Cocoon components - and I'd like to help write
these. For example, as Cocoon depends on many other projects it would be
great to have a suite of tests that we can automatically run to test new
versions of these other components. We could take the guesswork out of
testing new versions of Xalan/XSLTC!

Anyhow, attached is a .zip file with an eclipse project containing:

1. My MIDIGenerator and the unit testing TestMIDIGenerator.

2. Some cocoon helper classes I have created to help with unit testing.
These classes implement various cocoon environment interfaces. In
org.apache.cocoon.test.components there is a CocoonComponentManager, and in
org.apache.cocoon.test.environment there is a CocoonRequest, a CocoonSession
and a CocoonSourceResolver. These classes exist to make life easier when you
are instantiating and setting up components intended for use in Cocoon
outside of the Cocoon environment (e.g. in the Eclipse debug environment).
For example, the CocoonRequest allows you to pass request parameters as if
from the browser. 

3. A sample MIDI file (Bach's Prelude No.1 in C, BWV846a nicely sequenced by
B.R.Guillaud and available from http://www.classicalarchives.com/bach.html),
and the same file converted into XMidi. See http://www.palserv.com/XMidi/
for Peter Loeb's original java code to convert MIDI to XML, now modified to
be SAX-based, and a little about XMidi.

The Eclipse project dependencies are:

$COCOON_LIB/logkit-1.2.jar
$COCOON_LIB/excalibur-sourceresolve-2003071
$COCOON_LIB/excalibur-pool-1.2.jar
$COCOON_LIB/excalibur-logger-1.0.1.jar
$COCOON_LIB/avalon-framework-4.1.4.jar
$COCOON_LIB/excalibur-xmlutil-20030520.jar
$COCOON_LIB/excalibur-instrument-1.0.jar
$COCOON_LIB/excalibur-component-1.1.jar
$COCOON_LIB/cocoon-2.1rc2-dev.jar
$JUNIT_JAR
$JDOM_JAR

To give anyone not familiar with JUnit testing some code to look at, listed
below is the source for the TestMIDIGenerator. It also illustrates the use
of my cocoon helper classes. Actually, I have only one JUnit test in there -
that is: assertTrue(generator != null); but there could be many more to
provide a stronger indication that the tested code has worked (counting
generated nodes, checksumming the XML output string for a known value etc.).
Testing provides a nice way to create a set of fixed and repeatable tests
that can be executed automatically. Eclipse has built-in support for it. See
http://www.junit.org/index.htm for more on JUnit. Note also the use of JDOM
for pithy processing of XML (see http://www.jdom.org).

===========================================================================

package org.apache.cocoon.test.generation;

//import java.io.FileOutputStream;
import java.util.Map;

import junit.framework.TestCase;

import org.apache.avalon.framework.context.DefaultContext;

import org.apache.avalon.framework.logger.ConsoleLogger;
//import org.apache.avalon.framework.logger.NullLogger;

import org.apache.avalon.framework.parameters.Parameters;
import org.apache.cocoon.environment.ObjectModelHelper;
import org.apache.cocoon.environment.Request;
import org.apache.cocoon.generation.MIDIGenerator;
import org.apache.cocoon.util.HashMap;
import org.jdom.input.SAXHandler;
import org.jdom.output.XMLOutputter;

import org.apache.cocoon.test.environment.CocoonRequest;
import org.apache.cocoon.test.environment.CocoonSourceResolver;

/**
 * @author Mark Leicester
 */
public class TestMIDIGenerator extends TestCase
{
  public void testGenerate() throws Exception
  {
    System.out.println("Testing generate()");

    MIDIGenerator generator = new MIDIGenerator();
    assertTrue(generator != null);

    // Cocoon 2.1 Set the logger to NULL
    // NullLogger logger = new NullLogger();
    // Cocoon 2.1 Set the logger to the console
    ConsoleLogger logger = new ConsoleLogger();
    generator.enableLogging(logger);

    // Globally parameterize the generator
    Parameters globalParameters = new Parameters();
    globalParameters.setParameter("verbose", "true");
    generator.parameterize(globalParameters);

    // Create src string: the URI for the midi file
    String src = "samples/midi/Prelude.mid";

    // Locally parameterize the generator
    Parameters localParameters = new Parameters();

    // Create the parameters (simulating a URL)
    Map map = new HashMap();
    CocoonRequest r = new CocoonRequest();
    // r.setParameter("name", "value");
    map.put(ObjectModelHelper.REQUEST_OBJECT, (Request) r);

    // Create the resolver needed to locate the schema file
    CocoonSourceResolver resolver = new CocoonSourceResolver();
    resolver.enableLogging(logger);
    // Set up the root directory for the source resolver
    // System.getProperty("user.dir") = project home directory
    DefaultContext context = new DefaultContext();
    context.put("context-dir", System.getProperty("user.dir"));
    resolver.contextualize(context);

    // Setup the generator
    generator.setup(resolver, map, src, localParameters);

    // Create the JDOM sax handler, and make it the content handler
    SAXHandler sh = new SAXHandler();
    generator.setContentHandler(sh);

    // Perform the generation
    generator.generate();

    // Get the JDOM document and write it to the System.out
    XMLOutputter xo = new XMLOutputter();
    xo.setNewlines(true);
    xo.output(sh.getDocument(), System.out);

    // Alternatively, get the JDOM document and write it to a file
    // FileOutputStream fileStream = new FileOutputStream("output.xmi");
    // xo.output(sh.getDocument(), fileStream);
    // fileStream.close();

    return;
  }

}

===========================================================================

If you think this is a good approach then please, treat this code as your
own! In due course I shall attempt to create a MIDI block (including the
test class itself), but I am thinking that the unit testing helper classes
would go into the cocoon project itself so that everyone can use them? As is
the case with the attached Eclipse project, I always keep the code to be
tested separate from the unit tests so that building .jars remains easy. I
usually put my java source into /src/java and my test code into /src/test.

I hope this approach will help us make cocoon even more robust!

Regards,
Mark

Attachment: apache-cocoon.zip
Description: Binary data

Reply via email to