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




Reply via email to