You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@wicket.apache.org by Marek Gilbert <gi...@tacitknowledge.com> on 2007/07/20 04:12:58 UTC

Ajax + FormTester

Greetings.

I've been trying to figure out how to do a WicketTester based test of  
an Ajax-enabled form sequence.  The basic idea of the code under test  
is to have a bunch of panels on a single page, where navigation  
between panels is gated by server-side validation implemented as Ajax  
calls triggered from a submit button on each panel.

I'm using wicket 1.3 beta 1.

My first attempt at this was to do the obvious thing where I fill in  
the form using a FormTester and then call submit on the FormTester.   
This is almost right except that it doesn't end up calling the  
onSubmit or onError methods of my AjaxFormSubmitBehavior.  I now  
realize calling submit() here does an actual form submission, which  
is not what I want to test.

Then I noticed that there was an executeAjaxEvent method on  
WicketTester.  I then tried to fill in the form using a FormTester  
and then execute the "onclick" behavior associated with my button and  
this almost worked again.  This time all the ajax behavior events  
were being called but there were no values for my form parameters in  
the request.  This seems to be because  
BaseWicketTester.submitAjaxFormSubmitBehavior() pulls all the values  
out of the actual underlying form and sets parameters in the request  
based on them, in effect undoing the work I did with the FormTester  
(recall that FormTester sets up a request and then sets parameters on  
the request for each call to setValue or the like).

Similarly, I could forget using FormTester and instead call  
BaseWicketTester.setParameterForNextRequest() for each form value I  
care about, but using executeAjaxEvent again cancels this work out.

Then I tried using BaseWicketTester.executeBehavior(), but because  
this calls getServletRequest().setRequestToRedirectString(...) again  
the parameters that I've filled in with the FormTester get overwritten.

At this point, I have this almost working, but I feel like what I've  
done is an abomination.  I've subclassed FormTester and added  
ajaxSubmit methods, effectively an amalgamation of the  
FormTester.submit and the BaseWicketTester.executeAjaxEvent methods:

public void ajaxSubmit()
{
     ajaxSubmit(findSubmitButton(), "onclick");
}

public void ajaxSubmit(Component component, String event)
{
     try
     {
         MockHttpServletRequest servletRequest =  
ajaxWicketTester.getServletRequest();
         WebRequestCycle requestCycle =  
ajaxWicketTester.createRequestCycle();
         servletRequest.setRequestToComponent(getForm());
         // handle hasUploadedFiles()

         AjaxEventBehavior behavior = findAjaxEventBehavior 
(component, event);
         // handle behavior not found

         behavior.onRequest();
         requestCycle.getRequestTarget().respond(requestCycle);
     }
     catch (FileUploadException e)
     {
         throw new WicketRuntimeException(e);
     }
}

This gets me really close, except that I'm now finding behavioral  
differences between the site running in a container vs the unit  
test.  For example, when under test, error messages aren't being  
cleared between pages: once an error is generated it never goes  
away.  I could try to reach into RequestCycle.detach() for myself but  
this is going way outside the bounds of a reasonable solution to this  
problem.

So my questions come down to these:

   - Am I crazy for wanting to write these kinds of tests?  I figure  
a few tests like these running through critical site functionality  
should be integrated into our CI build with every commit.   
WicketTester tests are way more palatable than doing something like  
Selenium here.  When the forms weren't Ajax enabled the FormTester  
approach worked really well.  We don't have a whole lot of Javascript  
besides that which Wicket supplies so hooking up the ajax-based  
version of this test would seem to be just what the doctor ordered.

   - Why are executeBehavior and executeAjaxEvent implemented so  
differently?  executeBehavior uses setRequestToRedirectString then  
calls processRedirectCycle (which in turn calls detach and cleans up  
rendered error messages).  executeAjaxEvent calls onRequest on the  
behavior directly, and then calls requestCycle.getRequestTarget 
().respond(requestCycle) bypassing the detach step in the  
RedirectCycle, leaving error messages present forever.

   - Similarly, how could I effectively use executeAjaxEvent when  
submitAjaxFormSubmitBehavior pulls parameters from the underlying  
form?  Writing test code that reaches out and grabs the actual form  
to set fields, to have submitAjaxFormSubmitBehavior read these and  
set request parameters, to set my actual form fields seems really  
contorted.

   - All the Ajax-related calls in WicketTester are calling  
setupRequestAndResponse, which seems to be fundamentally at adds with  
what the FormTester is trying to do (it calls setupRequestAndResponse  
for itself and then manipulates parameters on the *current*  
request).  Would it possibly be better to have FormTester call  
setParameterForNextRequest?

   - (more of a question for wicket-user, but I've already got your  
attention) given all this, am I just missing something fundamental in  
how would I go about writing a test that fills in a form and clicks  
an AjaxSubmitButton?

If this issue is bugging other people I'd be happy to start hacking  
on FormTester and friends myself and submit patches but I was pretty  
sure that I didn't have the whole picture in my head yet and wanted  
to see if there were any obvious ways of doing this that I'm just  
missing.

Many thanks in advance and best regards for a killer tool!

-Gil

Re: Ajax + FormTester

Posted by Timo Rantalaiho <Ti...@ri.fi>.
Hello Gil,

That's a long email -- sorry if I missed something :) And 
anyway I skipped the parts on Wicket internals as I don't
know about that.

On Thu, 19 Jul 2007, Marek Gilbert wrote:
> I'm using wicket 1.3 beta 1.

Immediately upgrade to beta2, or better yet, to the
snapshots to get this

  http://issues.apache.org/jira/browse/WICKET-254

on board.

> Then I noticed that there was an executeAjaxEvent method on  
> WicketTester.  I then tried to fill in the form using a FormTester  
> and then execute the "onclick" behavior associated with my button and  
> this almost worked again.  This time all the ajax behavior events  

This is how it's supposed to be done, as far as I know.

> were being called but there were no values for my form parameters in  
> the request.  This seems to be because  
> BaseWicketTester.submitAjaxFormSubmitBehavior() pulls all the values  
> out of the actual underlying form and sets parameters in the request  
> based on them, in effect undoing the work I did with the FormTester  
> (recall that FormTester sets up a request and then sets parameters on  
> the request for each call to setValue or the like).

Yep. If I understand correctly, at least some of this is 
fixed in WICKET-254.

Wicket and its built-in ajax support are excellent, and so 
is WicketTester, but when doing heavy ajax we have faced a 
lot of problems with WicketTester. At least a part of them 
were solved by the above test.

>   - Am I crazy for wanting to write these kinds of tests?  I figure  

Of course not :)

> WicketTester tests are way more palatable than doing something like  
> Selenium here.  When the forms weren't Ajax enabled the FormTester  
> approach worked really well.  We don't have a whole lot of Javascript  
> besides that which Wicket supplies so hooking up the ajax-based  
> version of this test would seem to be just what the doctor ordered.

I think that WicketTester tests are more unit-test like
(even though they are not unit tests in the strictest
sense). Whatever you test with WicketTester, there's no
telling on how the code will behave in different browsers.
And sometimes it can be difficult to get something to work
realistically in the test.

That's why it's a very good idea to complement your
WicketTester tests with Selenium. This is most easily done
with Selenium RC (remote control) which you can nicely use
on a per-component basis, if you're on Java 5 (or above) and
can use WicketBenchTestCase from Wicket Bench:

  http://svn.laughingpanda.org/svn/wicket-bench/trunk/wicket-bench-test/src/test/java/test/DictionaryPanelTest.java

> If this issue is bugging other people I'd be happy to start hacking  
> on FormTester and friends myself and submit patches but I was pretty  
> sure that I didn't have the whole picture in my head yet and wanted  
> to see if there were any obvious ways of doing this that I'm just  
> missing.

Probably providing patches and quickstarts to Jira would get
stuff fixed quickly (maybe for 1.4 if 1.3 is starting to get
frozen), if you still have problems after the fix of
WICKET-254.


In general, it's a great idea to search the wiki

  http://cwiki.apache.org/WICKET/

and Nabble

  http://www.nabble.com/Wicket-f13974.html

before asking:

  http://www.catb.org/~esr/faqs/smart-questions.html#before


Best wishes,
Timo

-- 
Timo Rantalaiho           
Reaktor Innovations Oy    <URL: http://www.ri.fi/ >