cocoon-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Steve K <sh...@mm.st>
Subject Re: [RT] Unit testing and CocoonUnit
Date Sat, 01 Nov 2003 22:29:49 GMT
Upayavira --

This all looks great.  I've also been hacking at this problem and have a 
working solution that does not involve any open pipeline surgery or 
triple-pipeline-bypasses.  It is a bit of a hack and I do believe your 
solution is much more appropriate in the long term, however, maybe what 
I've done will give you some more ideas.

Originally I was using the CocoonBean to test pipelines, but I had some 
new pipelines that needed access to HTTP headers and cookies -- and I 
could not find a way to pass them in using the interfaces provided by 
the bean.  So I did the following:

- I extended the ExcaliburTestCase to create a CocoonTestCase.  In my 
new class I copied the initalize() and getClassPath() methods from the 
CocoonWrapper and modified it all enough so on JUnit setUp() I could 
instantiate an instance of Cocoon and stick it in a member variable. 
The reason I extended ExcaliburTestCase is that I have several Avalon 
components that my application uses that I also use to set up the test 
cases.

- To send a request to Cocoon, I create an HttpEnvironment object using 
mock servlet objects -- specifically the objects from 
http://strutstestcase.sourceforge.net/.  When I get the request back, I 
parse it into XML and use the XMLUnit style asserts on it.  Here is what 
the code looks like:

----

// set up the mock objects

ServletContextSimulator sctx = new ServletContextSimulator();
HttpContext hc = new HttpContext((ServletContext) sctx);
HttpServletRequestSimulator req = new HttpServletRequestSimulator(sctx);
HttpServletResponseSimulator res = new HttpServletResponseSimulator();

// set up the HTTP request

req.setHeader("Accept", text/xml,application/xml");
req.setHeader("Accept-Language", "en");
req.setParameterValue("cocoon-view", new String [] {"content"});

// output goes to baos

ByteArrayOutputStream baos = new ByteArrayOutputStream();
res.setOutputStream(out);

// set up HttpEnvironment to get the uri "foo/bar"

HttpEnvironment env = new HttpEnvironment(
	"foo/bar",
         "path/to/webapp",
         (HttpServletRequest) req,
         (HttpServletResponse) res,
         (ServletContext) sctx,
         hc,
         "",
         "");
env.enableLogging(new LogKitLogger(getLogger()));

// call process() on the cocoon member variable

boolean rv = cocoon.process(env);
baos.flush();

// parse the response into XML

SAXParser parser = (SAXParser) this.manager.lookup(SAXParser.ROLE);
DOMBuilder builder = new DOMBuilder();
parser.parse(new InputSource(
     new ByteArrayInputStream(baos.toByteArray())),
     new WhitespaceFilter(builder),
     builder);
Document xml = builder.getDocument();
this.manager.release((Component) parser);

// test the xml

assertXPathTrue(xml, "/blah");

----

This has worked pretty well so far.  I think extending the 
ExaliburTestCase and using mock servlet objects might be better than 
extedning HTTPUnit since it gives us more flexibility and more access to 
the raw Coccon request/response.

Some other things I've investigated on the way:

- Jakarta Cactus (http://jakarta.apache.org/cactus/) -- This takes a 
similar approach by setting up a mock servlet environment to test your 
servlets in.  However, it seemed way to big and complicated to do what I 
thought would be very simple.  Also, I think there is a lot to be gained 
by unit testing at the pipeline level as you suggest, rather than 
treating Cocoon like just another servlet.

- In my search for a mock implementation of the servlet classes, I came 
along MockObjects (http://www.mockobjects.com).  They have mock objects 
for much of the Java API, however, their approach didn't really fit into 
what I was trying to do.  Basically, you are supposed to pre-program the 
mock objects to expect certain method calls (e.g. getHeader should be 
called with such and such parameters, then getSession should be called, 
etc).  Since I just wanted mock objects good enough to masquerade as a 
servlet container and get the output from Cocoon, this was not very 
helpful.  However, perhaps a similar methodology could be adapted where 
you could pass in the expected XML for each pipeline step -- I think you 
  had this idea in our previous exchanges.

And finally, on a somewhat unrelated subject, one thing that I've always 
wanted Cocoon to do may be possible if support for collecting the XML at 
each pipeline step is added.  To aid in debugging, I think it would be 
very helpful to switch on some kind of debug mode, that would cause a 
trace of what pipeline steps where executed and the state of the XML at 
each step to be printed out at the bottom of each page you output to the 
browser.  This way it is easy for a developer to see the path though the 
pipelines the request took, as well as a snapshot of the XML each step 
of the way.

cheers,
-steve



Mime
View raw message