...
Code Block | ||||
---|---|---|---|---|
| ||||
...
<servlet>
<servlet-name>wicket</servlet-name>
<servlet-class>org.apache.wicket.protocol.http.WicketServlet</servlet-class>
<init-param>
<param-name>applicationFactoryClassName</param-name>
<param-value>org.apache.wicket.spring.SpringWebApplicationFactory</param-value>
</init-param>
<init-param>
<param-name>applicationBean</param-name>
<param-value>wicketApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
...
<!-- The SpringWebApplicationFactory will need access to a Spring Application context, configured like this... -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
...
|
...
Warning |
---|
Please note that you cannot use constructor argument based injection with Wicket, only accessorsetter-based injection! |
It's possible to have your annotated dependencies automatically injected on construction. For this you have to install a SpringComponentInjector in your application.
...
Code Block |
---|
class MyApplication extends WebApplication { public void init() { super.init(); addComponentInstantiationListener(new SpringComponentInjector(this)); } } class EditContact extends WebPage { @SpringBean private ContactDao dao; @SpringBean(name="userDao") private UserDao userDao; public EditContact(long userId) { ... } } |
With Wicket >1.5 it is:
Code Block |
---|
class MyApplication extends WebApplication {
public void init() {
super.init();
getComponentInstantiationListeners().add(new SpringComponentInjector(this));
}
}
|
Here the page (or indeed anything derived from a Wicket Component
) will have its dependencies injected when created. [Constructor/superclass chaining down to the Component(final String id, final IModel model) constructor, where there's a call to Here the page (or indeed anything derived from a Wicket {{Component}}) will have its dependencies injected when created. _\[Constructor/superclass chaining down to the Component(final String id, final IModel model) constructor, where there's a call to getApplication().notifyComponentInstantiationListeners(this);\]_ Wiki Markup
When doing this it is important to remember not to initialize dependencies, to null or any other value, e.g.private ContactDao dao=null;
. Don't do this because the injector will run before the subclass initializes its fields, and so the dao=null
will override the created proxy with null.
...
wicket-spring-annot
project provides the SpringComponentInjector class for you. All you have to do to get transparent injection working is to install the injector in your application like as shown above. SpringComponentInjector also supports automatic injection of wicket portlet apps.
...
Even when using automatic injection, unit testing is easy. Following is a sampel sample unit test that tests a fictional DeleteContactPage class.
...
Part 1 is the standard setup of the dependencies it is required to run the test. This particular test uses EasyMock library to make working with mock objects easier.
...
Part 3 is the setup of WicketTester and the SpringComponentInjector, which will inject our dao into classes which have the @SpringBean annotation
...
Code Block |
---|
public class DeleteContactPageTest extends TestCase {
public void test() throws ServletException {
// 1. setup dependencies and mock objects
Contact contact=new Contact();
MockControl daoCtrl=MockControl.createControl(ContactDao.class);
ContactDao dao=(ContactDao) daoCtrl.getMock();
daoCtrl.expectAndReturn(dao.load(10), contact);
dao.delete(10);
daoCtrl.replay();
// 2. setup mock injection environment
AnnotApplicationContextMock appctx=new AnnotApplicationContextMock();
appctx.putBean("contactDao", dao);
// 3. run the test
WicketTester app=new WicketTester();
// For wicket 1.3.5 use the code below
app.getApplication().addComponentInstantiationListener(new SpringComponentInjector(app.getApplication(), appctx))
// For wicket 1.2.5 use the code below.
//app.addComponentInstantiationListener(new SpringComponentInjector(app, appctx));
app.startPage(new DeleteContactPage(new DummyHomePage(), 10));
app.assertRenderedPage(DeleteContactPage.class);
app.assertComponent("confirmForm", Form.class);
app.assertComponent("confirmForm:confirm", Button.class);
app.setParameterForNextRequest("confirmForm:confirm", "pressed");
app.submitForm("confirmForm");
app.assertRenderedPage(DummyHomePage.class);
daoCtrl.verify();
}
}
|
If you are using a AuthenticatedWebApplication along with a SpringInjector, you can configure the WicketTester this way:
}
|
If you are using a AuthenticatedWebApplication along with a SpringInjector, you can configure the WicketTester this way:
Code Block |
---|
...
AuthenticatedWebApplication authenticatedWebApp = new MyAuthenticatedWebApplication() {
@Override
public void init() {
addComponentInstantiationListener(new SpringComponentInjector(this, myApplicationContext));
}
};
WicketTester tester = new WicketTester(authenticatedWebApp);
...
|
As you can see, the use of the AnnotApplicationContextMock removes some noise from the test case.
Unit Testing Proxy Approach with Custom Session
When unit testing a Wicket application with a custom session and with Spring bean dependencies, it best to create an mock implementation of a WebApplication, overriding the newSession method, and passing this to the WicketTester. Here is an exampleL
Code Block |
---|
public void testDefaultUnauthenticatedPage() throws Exception {
CatalogManager catalogManager = createMock(CatalogManager.class);
ApplicationContextMock appctx=new ApplicationContextMock();
appctx.putBean("catalogManager", catalogManager);
WicketTester app=new WicketTester(createMockApplication());
SpringComponentInjector componentInjector = new SpringComponentInjector(app.getApplication(), appctx);
app.getApplication().addComponentInstantiationListener(componentInjector);
app.startPage(HighSecurePage.class);
app.assertRenderedPage(LoginPage.class);
}
private static final WebApplication createMockApplication() {
return new WebApplication() {
@Override
public Class getHomePage() {
return HomePage.class;
}
|
Code Block |
... @Override AuthenticatedWebApplication authenticatedWebApp =public newSession MyAuthenticatedWebApplication(newSession(Request request, Response response) { @Override return new RCNSession(request); } public void init() { @Override protected ISessionStore newSessionStore() { addComponentInstantiationListener(return new SpringComponentInjectorHttpSessionStore(this, myApplicationContext)); } }; WicketTester tester = new WicketTester(authenticatedWebApp); ... |
As you can see, the use of the AnnotApplicationContextMock removes some noise from the test case.
}
|
To expand on the WicketTester/Spring integration, many web applications are developed on J2EE compliant servers and use the server based datasources and connection pooling accessed through a JNDI call. The datasource/connection pool is then centralized in a spring Spring configuration file as a spring bean. The implication on testing is that this bean is invalid since there is no JNDI service; hence a local datasource/connection pool bean is substituted. Once the ant script or manual method is defined to substitute the datasource bean the application context can be integrated as follows:
...