Saturday, January 3, 2009

Tidying Unit Test Output in Apache Wicket

I like nice tidy output from my unit tests.
I shouldn't see anything unless there is a failure. I run the tests many times a day, and it can be very disturbing to see errors, stack traces, or messages printed to the console. Only when a test fails do I want to see why it failed, which I can do from the comfort of my IDE.

Our test output should look something like this:
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running com.mycompany.TestHomePage
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.812 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
I started off with the Apache Wicket Quickstart, and the mvn clean install command produced output something like this:
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running com.mycompany.TestHomePage
INFO - Application - [WicketApplication] init: Wicket core library initializer
INFO - RequestListenerInterface - registered listener interface [RequestListenerInterface name=IBehaviorListener, method=public abstract void org.apache.wicket.behavior.IBehaviorListener.onRequest()]
INFO - RequestListenerInterface - registered listener interface [RequestListenerInterface name=IBehaviorListener, method=public abstract void org.apache.wicket.behavior.IBehaviorListener.onRequest()]

...

INFO - WebApplication - [WicketApplication] Started Wicket version 1.3.5 in development mode
********************************************************************
*** WARNING: Wicket is running in DEVELOPMENT mode. ***
*** ^^^^^^^^^^^ ***
*** Do NOT deploy to your live server(s) without changing this. ***
*** See Application#getConfigurationType() for more information. ***
********************************************************************
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.812 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
Log4j

One problem is obviously the log4j properties are writing out INFO messages. These logging messages might be needed while developing or perhaps in depoloyment. Normally you will have different logging levels for each, and have the logging configuration defined in the container that it runs in e.g. depoloyment logs only WARN, development logs INFO. This means I could remove the src/main/resources/log4j.properties file as it should not be deployed to either server.

Since we don't have a container when we run the build, log4j will complain
log4j:WARN No appenders could be found for logger (org.apache.wicket.protocol.http.pagestore.FileChannelPool).
log4j:WARN Please initialize the log4j system properly.
To stop this happening I want to tell log4j to be quiet when I run the tests. This can be done simply by putting a file in src/test/java/log4j.properties containing the single line
log4j.rootLogger=FATAL
Now our test output looks like this:
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running com.mycompany.TestHomePage
********************************************************************
*** WARNING: Wicket is running in DEVELOPMENT mode. ***
*** ^^^^^^^^^^^ ***
*** Do NOT deploy to your live server(s) without changing this. ***
*** See Application#getConfigurationType() for more information. ***
********************************************************************
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.765 sec
Wicket Development/Deployment Mode Testing

So now I have some text being written to the console by Apache Wicket. There are a couple of ways of dealing with this. One way is to override the method protected void outputDevelopmentModeWarning() in your WicketApplication. Though that would disable it for everyone, and I quite like seeing the message when I run Wicket in development mode.

I prefer instead to run my tests in Deployment mode. It doesn't matter if your test passes in development mode if the application doesn't work in deployment mode. The differences between the two modes are quite small anyway such as reloading of markup and stripping wicket tags.

How do I run our tests in production mode?
Well, I have a couple of choices here. In the test setup I could configure the mode.

public class TestHomePage {

private WicketTester tester;

@Before
public void setUp() {
tester = new WicketTester(new WicketApplication() {

@Override
public String getConfigurationType() {
return Application.DEPLOYMENT;
}

});
}
}
If I was going to follow this path, I would want to create a base tester class which can set this up for me, or create a TestingWicketApplication class which extends my WicketApplication and overrides this setting.

I prefer to leave the class alone as much as possible, so I am testing the class that will be deployed. Also I might mistakenly use WicketApplication instead of TestingWicketApplication in my tests.

So instead I set the deployment mode system property in the pom.xml, but only when running the tests.

I added this to my plugins in pom.xml

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemProperties>
<property>
<name>wicket.configuration</name>
<value>deployment</value>
</property>
</systemProperties>
</configuration>
</plugin>
Limitations
There are some limitations caused by deployment mode stripping tags or catching exceptions.

For example, Deployment mode strips the wicket tags by default, so if I want to use the WicketTester's TagTester I need to configure the application to leave the tags in place. I find I don't use the TagTester very often. Most tests are whether field rendered/submitted the correct value, or a button behaved correctly. I have used it to test for the correct behaviour when changing a CSS class on a div after a button was pressed or error message was displayed. For this single test I would just change the application settings before the page renders

application.getMarkupSettings().setStripWicketTags(false);
then I am free to use TagTester. This setting should only apply to this test, so it will go in the test method and not in the setup.

Another example of where this setting causes problems is in my first example post. I was testing that missing resources would throw an exception. Once I am in deployment mode, the application is still throwing an exception however Wicket catches the exception and renders an InternalErrorPage instead. So my test fails.

To fix this I have to either set the system property back to development mode
System.setProperty("wicket.configuration", Application.DEVELOPMENT);
so that the exception is thrown, or override the WicketApplication for this test. Setting the system property would make the test pass, but then the development error message is displayed, so I will override the WicketApplication to turn on development mode and disable the message.

public class MissingResourcesTestPageTestCase {

private WicketTester tester;

@Before
public void setup() {
final WicketApplication application = new WicketApplication() {
@Override
public String getConfigurationType() {
return Application.DEVELOPMENT;
}
@Override
protected void outputDevelopmentModeWarning() {
// don't display message
}
};
tester = new WicketTester(application);
}

@Test(expected = MissingResourceException.class)
public void testMissingResourcePage() throws Throwable {
try {
tester.startPage(MissingResourcesTestPage.class, new PageParameters());
}
catch (final WicketRuntimeException wre) {
throw wre.getCause();
}
}
}
Now when we mvn clean install we get this output

-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running com.mycompany.TestHomePage
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.719 sec
Running com.mycompany.MissingResourcesTestPageTestCase
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.156 sec

Results :

Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
Looking good!

Tuesday, December 23, 2008

Lest We Forget

I have been working on a German web application for the past 2 years, so that means I end up dealing with message keys a lot. Where possible it is nice to have these resources in the markup file and not cluttering up the java code (unless the text has some dynamic component to it). The designer can move the text around and style it as they please.

In Apache Wicket we can happily use tags in the markup to fetch the localised message text. That is all good until you deploy it to production and someone has forgotten or mistyped the message key. The default Apache Wicket settings mean you won't get an error message, instead default text in the tag will be shown. If your developers have been putting in text that is almost right, you might not notice the problem. If they have used rude jokes, well, let's hope you notice them.

Or we could go for a Test-Driven Development approach. Let's make the application fail quickly in development if the message key is missing. No more forgetting.

First, we can create a jUnit test here /src/test/java/[package]/MissingResourcesTestPageTestCase.java

import java.util.MissingResourceException;

import org.apache.wicket.PageParameters;
import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.util.tester.WicketTester;
import org.junit.Before;
import org.junit.Test;

public class MissingResourcesTestPageTestCase {

private WicketTester tester;

@Before
public void setup() {
tester = new WicketTester(new WicketApplication());
}

@Test(expected = MissingResourceException.class)
public void testMissingResourcePage() throws Throwable {
try {
tester.startPage(MissingResourcesTestPage.class, new PageParameters());
}
catch (final WicketRuntimeException wre) {
throw wre.getCause();
}
}
}
We could have changed the test to expect a WicketRuntimeException, but these can occur for many different reasons, such as the markup not existing at all. We want the test to fail if the resource could not be found.

Sometimes I prefer creating the basic page first. Just the constructor so we can reference it from the unit test. The MoreUnit plugin for Eclipse can even create the jUnit test from the page with a keyboard shortcut. We can write this test first then use our IDE to create the /src/test/java/[package]/MissingResourcesTestPage.java file

import org.apache.wicket.markup.html.WebPage;

public class MissingResourcesTestPage extends WebPage {

public MissingResourcesTestPage() {
//do nothing to test markup rendering
}
}
Running the test now will fail because the markup does not exist. You can create an empty file called /src/test/java/[package]/MissingResourcesTestPage.html and re-run the tests and it should still fail.

Finally, add the markup

<html>
<body>
<wicket:message key="this.resource.key.is.missing">this message resource is missing for testing purposes</wicket:message>
</body>
</html>
and run the test again. red. Wicket is working correctly and displaying the default text. So let's change it to start throwing exceptions.

Alter your /src/main/java/[package]/WicketApplication.java to override the init() method as follows:

public class WicketApplication extends WebApplication {
@Override
protected void init() {
super.init();
getResourceSettings().setUseDefaultOnMissingResource(false);
getResourceSettings().setThrowExceptionOnMissingResource(true);
}
}

setUseDefaultOnMissingResource(false) will mean Wicket won't try to use the default text inside the wicket:message tag when it can't find the resource key. Now it will just display nothing, which won't help our future tests. To cause an exception to be thrown you need to call setThrowExceptionOnMissingResource(true).

Run the test. Yay, green bar!
Done, done, done!

Anytime in the future that one of our developers creates a page and makes a typo, forgets to add the resource key, deletes a key, renames the resource file etc they will get a nice failed message when they run the tests. As long as they create tests to render the page. All developers do that, don't they?

Pretty Source Code

Ok, I have set up the syntax highlighter so I can display code like this

public class Abc {
public Abc() {
int xyz = 10;
}
}

Monday, December 22, 2008

First Post!

I have got to get this first post out of the way so I can start talking about more interesting things. So here goes...

I thought I would start a blog to share my experiences over the past few years with Test-Driven Development (TDD) and Apache Wicket. No doubt it will wander off into other areas such as Agile, Scrum, and Continuous Integration.

Ok, first post is done. done. done!