Since the development of our own web applications is mostly based on a Spring framework for dependency injection and application configuration in general and Apache Wicket framework as a truly component-oriented web framework for the MVC part of building a web application, it’s especially important for us to get these two frameworks running together smoothly not only when deployed on a running server instance itself but rather during the execution of our JUnit based integration tests as well. Thanks to the WicketTester API provided by the Wicket framework itself, one can easily build high-quality web applications while practicing test driven development and providing a decent set of unit and integration tests to be executed with each build. As already mentioned previously, integration and configuration of our web applications is based on a lightweight Spring container meaning that the integration of Spring’sApplicationContext and a WicketTester API is essential to get our integration tests running. In order to best explain how to achieve that integration in an easy and elegant fashion in your integration test environment, we’ll first take a look at a configuration of these 2 framework beauties in a runtime environment.
In order to get Wicket framework up to speed when your server is up and running, you usually configure an according WicketFilter instance in your web application deployment descriptor file (web.xml) while passing it a single init parameter called applicationClassName that points to your main implementation class extending org.apache.wicket.protocol.http.WebApplication where all of your application-wide settings and initialization requirements are dealt with:
In case you want to get Wicket application up and running while leaving the application configuration and dependency injection issues to the Spring container, the configuration to be provided within the deployment descriptor looks slightly different though:
The additional configuration part containing listener and context parameter definition is a usual, purely Spring container related configuration detail. ContextLoaderListener is an implementation of standard Servlet API ServletContextListener interface provided by the Spring framework itself and is responsible for looking up an according bean definition file(s) specified by the context param above and creating an ApplicationContext instance during servlet context initialization accordingly. When integrating an ApplicationContext instance with Wicket framework, one of the beans defined in the above mentioned Spring bean definition file has to be your own specific extension of org.apache.wicket.protocol.http.WebApplication. You can either define an according bean in the bean definition file itself:
or use powerful classpath scanning feature of the Spring framework and annotate the MyWebApplication implementation with the appropriate @Component annotation accordingly while enabling the Spring container to scan the according package(s) of your application for relevant bean definitions:
Either way, if everything goes well, you’ll get a pre-configured ApplicationContext all set up during the startup of your web container. One of the beans in the ApplicationContext will be your own extension of Wicket’s WebApplication type. SpringWebApplicationFactory implementation provided by the Wicket framework itself that you have defined as theapplicationFactoryClassName in the configuration of your WicketFilter will then be used in order to retrieve that very same WebApplication bean out of your Spring ApplicationContext. The Factory expects one and only one extension of Wicket’s very own WebApplication type to be found within the ApplicationContext instance at runtime. If no such bean or more than one bean extending WebApplication is found in the given ApplicationContext an according IllegalStateException will be raised and initialization of your web application will fail:
After the WebApplication bean has been successfully retrieved from the ApplicationContext via SpringWebApplicationFactory, WicketFilter will then, as part of its own initialization process, trigger both internalInit() and init() methods of the WebApplication bean. The latter one is the exact spot where the last piece of the runtime configuration puzzle between Wicket and Spring is to be placed :
SpringComponentInjector provided by the Wicket framework enables you to get dependencies from the ApplicationContext directly injected into your Wicket components by simply annotating these with the according @SpringBean annotation.
One of the main features of Apache Wicket framework is the ability to easily write and run plain unit tests for your Pages and all other kinds of Components that even include the verification of the rendering process itself by using JUnit framework and the WicketTester API only. When using Spring framework for application configuration together with Wicket, as we do, you can even use the same tools to easily write and run full blown integration tests for your web application as well. All you have to do is use Spring’s TestContext framework additionally to configure and run your JUnit based integration tests. The Spring Framework provides a set of Spring specific annotations that you can use in your integration tests in conjunction with the TestContext framework itself in order to easily configure an according ApplicationContext instance for your tests as well as for appropriate transaction management before, during and after your test execution. Following code snippet represents a simple JUnit 4 based test case using Spring’s specific annotations in order to initialize an ApplicationContext instance prior to executing the test itself:
By defining three annotations on the class level (see code snippet above) in your test, Spring’s TestContext framework takes care of preparing and initializing an ApplicationContext instance having all the beans defined in the according Spring context file as well as the transaction management in case your integration test includes some kind of database access. Fields marked with @Autowired annotation will be automatically dependency injected as well so that you can easily access and use these for your testing purposes. Since MyWebApplication, which extends Wicket’s WebApplication type and represents the main class of our web application, is also a bean within the ApplicationContext managed by Spring, it will also be provided to us by the test framework itself and can be easily used in order to initialize a WicketTester instance later on during the execution of the test’s setUp() method. With this kind of simple, annotation based test configuration we are able to run an integration test that verifies whether a LoginPage gets started and initialized, whether the rendering of the page runs smoothly and whether the page itself contains a LoginComponent that we possibly need in order to process user’s login successfully.
When you run this test though, you’ll get the following exception raised unfortunately:
As you can see above, the Exception gets raised during the initialization of the WicketTester instance even before the actual test method gets executed. Even though we have applied rather cool and simple annotation based test configuration already described and passed in perfectly well prepared ApplicationContext instance to the WicketTester instance in the constructor, somewhere down the rabbit hole someone complained that no WebApplicationContext instance could have been found which seems to be required in order to initialize the WicketTester properly.
The problem that we run against here is due to the fact that SpringComponentInjector during its own initialization is trying to get hold of an according Spring’s ApplicationContext instance that would normally be there in a runtime environment but does not find any since we are running in a test environment currently. SpringComponentInjector delegates to Spring’s own WebApplicationContextUtils class to retrieve the instance of ApplicationContext out of the ServletContext which is perfectly fine for a runtime environment but is unfortunately failing in a test environment:
If you still remember we defined a ContextLoaderListener in our web.xml file as part of the configuration of our runtime environment that makes sure an according WebApplicationContext instance gets initialized and registered against the ServletContext properly. Luckily, this problem can easily be solved if we slightly change the way we initialize SpringComponentInjector in our main MyWebApplication class. Apart from the constructor that we have used so far, there is another constructor in the SpringComponentInjector class that expects the caller to provide it with an according ApplicationContext instance rather than trying to resolve one on its own:
In order to use this constructor instead of the one we used previously, we now obviously need to get hold of the ApplicationContext instance on our own in our MyWebApplication implementation. The easiest way to do this is to use Spring’s own concept of lifecycle callbacksprovided to the beans managed by the Spring container. Since our MyWebApplication is also a bean managed by the Spring container at runtime (enabled by the classpath scanning and @Component annotation on a type level), we can declare it to implement ApplicationContextAware interface which ensures that it gets provided with the ApplicationContext instance that it runs in by the Spring container itself during startup.
So the relevant parts of MyWebApplication type will now look something like the following code snippet:
For additional clarification of how MyWebApplication now relates to both Wicket and Spring framework here is an according class diagram:
And that’s all folks! No additional modifications are required to the test itself. It’s gonna turn green now. This way you can use exactly the same Spring context configuration that you’d use in your runtime environment for running your JUnit based integration tests as well. Just grab the colleague you like most and try a pair programming and test first approach with Wicket and Spring. You’ll be delighted! And if you’re not, go buy yourself one of these ;).