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);
}
}
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.