Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

...

Code Block
xml
xml
...
<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 Wiki MarkupHere 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);\]_

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.

Using annotation-based approach, you should not worry about serialization/deserialization of the injected dependencies as this is handled automatically, the dependencies are represented by serializable proxies. Also, you should not mark you your dependency properties transient because if you do so, they won't be re-initialized upon deserialization.

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:

...