Whenever a defect is found in my application, my first thought is always the same — I SUCK.
After that wears off in 10 seconds, I move on to wondering how I let it slip through the cracks. Often before even diagnosing what the fix would be, I jump into my unit tests and start coding up a test that fails under this scenario. I have moved so steadily towards this practice, that is is rare for me to even try and demonstrate the issue in the running app itself. So I often code the test, get it to fail on the existing code, and then finally — make the fix and the test passes. End of story. That defect will not return because I have a test that demonstrates the correct behavior now.
However, sometimes you just can’t get the test to fail. It is often easy to give up and skip the test. Who’s going to notice! But you have to dig deeper. Today, I found a good example of that and learned something.
WicketTester has a method called assertModelValue(). We tend to use this a lot and never had any issues. I was trying to write some tests for a nasty visibility bug and I used this guy to assert some values on the page. As it turned out, my should-be-failing tests were not failing. I couldn’t figure out why. Upon comparing the model values with the output of the page dump, I noticed they were different. As it turns out, there is a nasty assumption here that we have been making.
When you call assertModelValue() on a LoadableDetachableModel that has been detached, it will reload the model to get the value and it will NOT detach it.
2 problems there:
1. The load — In our case, I needed the model value of the page that I just rendered. Here is some code.
tester.startPage(ResultsPage.class); tester.assertRenderedPage(ResultsPage.class); tester.assertNoErrorMessage(); tester.dumpPage(); tester.assertModelValue("myComponent", "foobar");
In that example, we will be able to check the value of the component in 2 spots: the page dump output and the assertModelValue call. The essence of the problem is that these values can be different! dumpPage() will show you the value that the unit test user really saw on startPage(). However, given you have a LoadableDetachableModel in place for this component, that value is now gone! Its unloaded before startPage() even returns. Now, when you call assertModelValue(), it sees it needs to load and does so getting you a new value! That might be the same value as before of course but if the value is the number of nanoseconds since the earth began it will be different than what you saw in dumpPage(). Anytime the model value can change from load to load, you can’t rely on assertModelValue like this.
2. The detach — After you call assertModelValue, your component is going to hold on to your new value — FOREVER! Or until somebody detaches it which usually happens after the next page render. So your next page render is going to just use your bad model value throughout and clear it on detach().
What’s the solution?
I still like assertModelValue’s potential and might use it here and there but will be more cautious. I think the only way I was able to do what i needed to make my test case fail was:
This assert method basically looks at the HTML and pattern matches what you gave it. It is a consistent performer because you don’t run into load/detach issues. The downside is that it isn’t so smart. What if foobar appears multiple times on your page? Well, you better come up with a better pattern! Surround the value with the HTML tags that include the wicket:id of the component. Surely, you can make it more robust.