cocoon-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Upayavira ...@upaya.co.uk>
Subject Re: [RT] Unit testing and CocoonUnit
Date Sun, 02 Nov 2003 08:03:46 GMT
Steve K wrote:

> 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.

I suspect that your and my ideas potentially complement each other. 
Let's see...

> 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:

It would probably be my intention to extend the bean to be able to 
handle an HTTPEnvironment of some sort, which would make this possible.

> - 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.

Effectively you wrote your own tool to invoke Cocoon, which is fair 
enough. I'd like to see this sharing as much of the existing code as 
possible, though.

> - To send a request to Cocoon, I create an HttpEnvironment object 
> using mock servlet objects -- specifically the objects from 
> http://strutstestcase.sourceforge.net/.  

Useful to know about.

> When I get the request back, I parse it into XML and use the XMLUnit 
> style asserts on it.  

Good that we share on something!

> 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");
>
> ----

Very interesting.

The thing I liked about HTTPUnit was the relatively high level way in 
which a testing script was described. I would like to see the testing 
code being somewhat higher level than the code you've got. For example, 
the parsing of XML, and creation of an environment done behind the 
scenes. But I suspect that what I'm talking about is merely a wrapper 
around what you've done already.

To extend your solution to do the pipeline inspection I was talking 
about, you'd need to create an HTTPTestEnvironment, which puts an empty 
ArrayList into the ObjectModel (not hard). Then the pipeline code would 
pick up on this and insert the UnitTestTransformer in between each stage 
of the pipeline. Once you've done cocoon.process(env), you can get at 
the DOM objects stored in this ArrayList via the environment's object 
model. Make sense?

> This has worked pretty well so far.  

Great.

> 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.

 From my brief reading up on HTTPUnit, it offers two ways to run tests, 
using an actuall HTTP request, or by running a servlet within its own 
simulated servlet context. My proposed extension to HTTPUnit would be to 
add another method - via Cocoon itself. The code to do that would be 
much the same as you have it for your ExcaliburTestCase, and would offer 
the user access to Cocoon data if they wanted it (but it would be hidden 
unless needed).

What I like about HTTPUnit is its rich language for describing web 
conversations. And that is just as relevant to Cocoon as it is servlets 
or HTTP testing, it is just the access method that is different.

> 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.

I skimmed it too, but it didn't seem to fit.

> - 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.

I haven't thought about this side of things that much - I guess I would 
create my own objects that implement o.a.c.environment.Request/Response, 
etc, e.g. HTTPUnitTestEnvironment, rather than using mocks.

> 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.

I've never looked at it (and probably should), but I think the 
logTransformer does that, although it isn't transparent, which is what 
I'm proposing.

Thanks for taking the time to respond to my message. I suspect that if 
we work together on this one, we could come up with something really neat.

Regards, Upayavira



Mime
View raw message