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!

No comments:

Post a Comment