Search code examples
javaunit-testingjunit4wicket

Wicket Ajax test resetting DropDownChoice to null


I am trying to set up a unit test for an Ajax behavior that I have added to a DropDownChoice component. When I make a call to tester.executeAjaxEvent(…) the DropDownChoice model value gets reset to “null”. I am not understanding something fundamental on how ajax works in wicket. Can anybody help me out here? Why is triggering the "change" event, causing the component to set it's model to null.

DropDownPage.html

<?xml version="1.0" encoding="UTF-8"?>
<html>
    <head>
        <title>DropDownPage</title>
    </head>
    <body>
        <form wicket:id="form">
            <select wicket:id="dropDown">
                <option>Option 1</option>
            </select>
        </form>
    </body>
</html>

DropDownPage.java

public class DropDownPage extends WebPage {
    private static final Logger log = LogManager.getLogger(DropDownPage.class);

    public DropDownPage() {
        Form<Void> form = new Form<Void>("form");
        add(form);
        
        DropDownChoice<String> dropDown = new DropDownChoice<String>("dropDown", new Model<String>(new String()), Arrays.asList(new String[] { "A", "B" }));
        dropDown.setOutputMarkupId(true);

        dropDown.add(new AjaxFormComponentUpdatingBehavior("change") {
            @Override
            protected void onUpdate(AjaxRequestTarget target) {
                String choice = dropDown.getModelObject();
                if (choice == null) {
                    nullCall(target);
                    return;
                }
                
                switch (choice) {
                case "A":
                    doAStuff(target);
                    break;
                case "B":
                    doBStuff(target);
                    break;
                default:
                    unknownType(target);
                }
            }
    });
        form.add(dropDown);
    }

    protected void doAStuff(AjaxRequestTarget target) {
        log.info("doAStuff(...)");
    }

    protected void doBStuff(AjaxRequestTarget target) {
        log.info("doBStuff(...)");
    }
    
    protected void nullCall(AjaxRequestTarget target) {
        log.info("nullCall(...)");
    }
    
    protected void unknownType(AjaxRequestTarget target) {
        log.info("unknownType(...)");
    }
}

TestDropDownAjax.java

public class TestDropDownAjax {
    private WicketTester tester;
    
    @Before
    public void setup() throws Exception {
        tester = new WicketTester();
    }
    
    @After
    public void tearDown() {
        tester.destroy();
    }
    
    int aCount = 0;
    int nullCount = 0;
    
    @SuppressWarnings("unchecked")
    @Test
    public void testDropDownPage() {
        DropDownPage dropDownPage = new DropDownPage() {
            @Override
            protected void doAStuff(AjaxRequestTarget target) {
                super.doAStuff(target);
                ++aCount;
            }
            
            @Override
            protected void nullCall(AjaxRequestTarget target) {
                super.nullCall(target);
                ++nullCount;
            }
        };
        
        DropDownChoice<String> dropDown = (DropDownChoice<String>) dropDownPage.get("form:dropDown");
        assertNotNull(dropDown);
        
        List<String> choices = (List<String>) dropDown.getChoices();
        String choice = choices.get(0);
        
        dropDown.setModelObject(choice);
        
        tester.startPage(dropDownPage);
        
        tester.assertModelValue("form:dropDown", dropDown.getModelObject());
        
        assertEquals(choice, dropDown.getModelObject());

        assertEquals(0, nullCount);
        assertEquals(0, aCount);
        
        tester.executeAjaxEvent("form:dropDown", "change");
        
        assertEquals(0, nullCount);     // fails here
        assertEquals(1, aCount);
    }
}

Solution

  • The AjaxFormComponentUpdatingBehavior, prior to calling your update method, calls the internal method inputChanged. This method tries to turn the latest user input into a new value for the dropdown's model. Since you didn't actually input anything new, this will be interpreted as selecting the empty option, causing the model value to become null.

    As such, you need to do the form input through WicketTester as well.

    One way to do this, is through FormTester:

    FormTester formTester = tester.newFormTester("form");
    formTester.select("dropDown", 0);
    tester.executeAjaxEvent("form:dropDown", "change");
    

    This will make your test pass.