views:

511

answers:

3

So, I've spent a couple of hours first trying to "fix" this myself and then Googling like a madman but didn't find anything that would've helped so now I'm here.

Basically I have a custom Panel within Wicket's own ModalWindow and since I like unit testing, I want to test it. The specific behavior here is refreshing the ModalWindow's content: In my actual code from where I extracted this the Ajax event handling actually reloads new stuff to the content panel, I just removed those to make this shorter.

So, here's the Panel's code

package wicket.components;

import org.apache.wicket.ajax.AjaxRequestTarget;

import org.apache.wicket.ajax.markup.html.form.AjaxButton;
import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.*;

public class MyModalWindowPanel extends Panel {

    private Form form;
    private ModalWindow modal;

    public MyModalWindowPanel(String id, ModalWindow modal) {
        super(id);

        this.setOutputMarkupId(true);
        this.modal = modal;

        initializeForm();    
        addBasicDataFieldsToForm();    
        add(campaignForm);
    }

    private void initializeForm() {
        form = new Form("form");
        form.setOutputMarkupId(true);
    }

    private void addBasicDataFieldsToForm() {
        campaignForm.add(new AjaxButton("infoSubmit",
                         new Model<String>("Ajaxy Click")) {

            protected void onSubmit(AjaxRequestTarget target, Form<?> form) {

                modal.setContent(new MyModalWindowPanel(modal.getContentId(),
                                                        modal));

                modal.show(target);
            }
        });
    }
}

and the corresponding markup

<wicket:panel>
    <form wicket:id="form">
     <input type="submit" value="Ajaxy Click" wicket:id="infoSubmit" />
    </form>
</wicket:panel>

Do note that when run in servlet container such as Tomcat, this works correctly - there's no functional bugs here!

So what's the problem then? I'm not seemingly able to get the unit test for this to work! My test class for the panel looks like this

package wicket.components;

import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.util.tester.*;
import junit.framework.TestCase;

public class MyModalWindowPanelTestCase extends TestCase {

    private WicketTester tester;    
    private ModalWindow modal;

    @Override
    protected void setUp() throws Exception {
        tester = new WicketTester();    
        modal = new ModalWindow("modal");

        tester.startPanel(new TestPanelSource() {

            public Panel getTestPanel(String id) {    
                return new MyModalWindowPanel(id, modal);
            }
        });
    }

    public void testReloadingPanelWorks() throws Exception {
        // the next line fails!
        tester.executeAjaxEvent("panel:campaignForm:campaignInfoSubmit",
                                "onclick");
        tester.assertNoErrorMessage();
    }
}

and here's the resulting stacktrace of running that

java.lang.IllegalStateException: No Page found for component [MarkupContainer [Component id = modal]]
    at org.apache.wicket.Component.getPage(Component.java:1763)
    at org.apache.wicket.RequestCycle.urlFor(RequestCycle.java:872)
    at org.apache.wicket.Component.urlFor(Component.java:3295)
    at org.apache.wicket.behavior.AbstractAjaxBehavior.getCallbackUrl(AbstractAjaxBehavior.java:124)
    at org.apache.wicket.ajax.AbstractDefaultAjaxBehavior.getCallbackScript(AbstractDefaultAjaxBehavior.java:118)
    at org.apache.wicket.ajax.AbstractDefaultAjaxBehavior.getCallbackScript(AbstractDefaultAjaxBehavior.java:106)
    at org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow$WindowClosedBehavior.getCallbackScript(ModalWindow.java:927)
    at org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow.getWindowOpenJavascript(ModalWindow.java:1087)
    at org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow.show(ModalWindow.java:352)
    at wicket.components.MyModalWindowPanel$1.onSubmit(MyModalWindowPanel.java:45)
    at org.apache.wicket.ajax.markup.html.form.AjaxButton$1.onSubmit(AjaxButton.java:102)
    at org.apache.wicket.ajax.form.AjaxFormSubmitBehavior.onEvent(AjaxFormSubmitBehavior.java:143)
    at org.apache.wicket.ajax.AjaxEventBehavior.respond(AjaxEventBehavior.java:177)
    at org.apache.wicket.ajax.AbstractDefaultAjaxBehavior.onRequest(AbstractDefaultAjaxBehavior.java:299)
    at org.apache.wicket.util.tester.BaseWicketTester.executeAjaxEvent(BaseWicketTester.java:1236)
    at org.apache.wicket.util.tester.BaseWicketTester.executeAjaxEvent(BaseWicketTester.java:1109)
    at wicket.components.MyModalWindowPanelTestCase.testReloadingPanelWorks(MyModalWindowPanelPanelTestCase.java:31)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at junit.framework.TestCase.runTest(TestCase.java:168)
    at junit.framework.TestCase.runBare(TestCase.java:134)
    at junit.framework.TestResult$1.protect(TestResult.java:110)
    at junit.framework.TestResult.runProtected(TestResult.java:128)
    at junit.framework.TestResult.run(TestResult.java:113)
    at junit.framework.TestCase.run(TestCase.java:124)
    at junit.framework.TestSuite.runTest(TestSuite.java:232)
    at junit.framework.TestSuite.run(TestSuite.java:227)
    at org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:81)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:46)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

So, how can/should I fix my unit test so that it would pass?

+2  A: 

I'm gonna go out on a limb here and say: Add your Panel component to a Page for testing..

AFAIK you can't test individual components, but should set up a test by getting a page and doing asserts on that..

This is what I use for testing:

public class TestHomePage {
    private static WicketTester tester;

    @BeforeClass
    public static void setUp() {
     tester = new WicketTester(new WicketApplication() {
      @Override
      protected void init() {
       //Override init to use SpringUtil's SpringContext due to missing WebApplicationContext
       addComponentInstantiationListener(new SpringComponentInjector(this, SpringUtil.getContext()));
      }
     });
    }

    @Test
    public void testRenderMyPage() {
     //start and render the test page
     tester.startPage(HomePage.class);

     //assert rendered page class
     tester.assertRenderedPage(HomePage.class);

     //assert page contents
     tester.assertContains("Welcome to my webpage");
    }
}

Please do correct me if I'm wrong!

Tim
`WicketTester#startPanel()` actually adds the given panel to `DummyPanelPage` with the id `panel` to enable testing it as the single component on the page. I did however try using `Page` as `ModalWindow`'s content but that didn't work either.
Esko
Ah ok, sorry I missed that.. Have you tried going around the test setup by keeping your modalwindowpanel as it is, and adding it to a `MyTestPage extends WebPage`? The WicketTester().startPanel does not seem to work from what you're telling me/us..
Tim
Time flies, sorry :) Yes, I tried that too and got even more obscure errors.
Esko
Hmm, just to eliminate any variables, have you tried UnitTesting the unaltered ModalWindow?
Tim
I finally decided to accept this as answer even though the problem still persists; I'm assuming the issue here is actually a defect in the component itself and after I had extensively tested this I noticed that Wicket doesn't actually replace the component's contents but instead creates a new modal component on top of the existing one without closing the old cleanly. Oh well.
Esko
+1  A: 

The "production" code shown that ostensibly works is inconsistent. I suspect the variable campaignForm is supposed to be the field form.

But I think the root problem is that the modal window itself is nowhere actually attached to a component, and thus cannot be rendered.

If in the real code, it's attached somewhere outside your MyModalWindowPanel component, you'll definitely need to attach it somewhere in the test as well, likely by making your test build either a test page or a test panel that contains both the modal window and the component under test.

Don Roby
+1  A: 

The only time I've gotten that "No Page Found" exception was when trying to call getPage() in a panel constructor. The constructor is executed before the panel gets added to a page, so nothing is found... Are you perhaps doing something like that? Perhaps this code works in production, but is still throwing that error behind the scenes?

I know it's not what you are looking for, but I've found Selenium to be a great tool for testing web UI's. JUnit is awesome for testing logic, but Selenium is better suited for making sure the right thing displays in your UI. It looks hinky with it's gui interface and such, but it works great, and if you dig a bit the scripting api is easily accessible.

perilandmishap
That was my initial reaction too and I made sure that's not the problem here. I don't know if `ModalWindow` internals do that though, I think I'll have to check that next.
Esko