package com.ttdev.wicketpagetest;
import java.lang.annotation.Annotation;
import org.apache.wicket.Application;
import org.apache.wicket.Component;
import org.apache.wicket.application.IComponentInstantiationListener;
import org.apache.wicket.injection.Injector;
import org.apache.wicket.protocol.http.WebApplication;
/**
* Use this class to install an injector that can inject mocked objects before
* considering the real beans. This is usually done in
* {@link Application#init()}. Then you can provide mocked objects by calling
* {@link #mockBean(String, Object)} usually in your unit tests.
*
* @author Kent Tong
*
*/
public class MockableBeanInjector extends Injector implements
IComponentInstantiationListener {
private MockedBeanFieldValueFactory mockedBeansFactory;
private Injector originalInjector;
/**
* @param injectionAnnotClass
* the injector will handle fields with this annotation. This is
* usually SpringBean.class or Inject.class.
* @param originalInjector
* the original injector to be used in production (e.g., to look
* up Spring beans).
*/
public MockableBeanInjector(
Class<? extends Annotation> injectionAnnotClass,
Injector originalInjector) {
this.mockedBeansFactory = new MockedBeanFieldValueFactory(
injectionAnnotClass);
this.originalInjector = originalInjector;
}
/**
* Create an instance that can inject into fields with the {@link Mock}
* annotation and that has no chained injector. You should use this
* constructor if you aren't using Spring nor Guice.
*/
public MockableBeanInjector() {
this(Mock.class, null);
}
/**
* Install the specified MockableBeanInjector into the specified webapp.
*
* @param webapp
* the webapp
* @param mockableBeanInjector
* the injector to be installed.
*/
public static void installInjector(WebApplication webapp,
MockableBeanInjector mockableBeanInjector) {
mockableBeanInjector.bind(webapp);
webapp.getComponentInstantiationListeners().add(mockableBeanInjector);
}
private static MockedBeanFieldValueFactory getMockedBeanFactory() {
Injector injector = Injector.get();
if (injector == null || !(injector instanceof MockableBeanInjector)) {
throw new RuntimeException(
"You must call MockableBeanInjector.installInjector() "
+ "in your WebApplication's init()");
}
MockableBeanInjector mbInjector = (MockableBeanInjector) injector;
return mbInjector.mockedBeansFactory;
}
/**
* Add a mocked object for the specified field into the instance of this
* class which has been installed as the injector for the application. When
* a page containing such a named field with the annotation, the mocked
* object will be injected into there.
*
* @param fieldName
* the name of the field
* @param mockedBean
* the mocked object
*/
public static void mockBean(String fieldName, Object mockedBean) {
getMockedBeanFactory().mockBean(fieldName, mockedBean);
}
/**
* Remove all mocked objects.
*/
public static void clearMockBeans() {
getMockedBeanFactory().clearMockedBeans();
}
/**
* Called by Wicket to inject field values into the object. Here it will
* consider the mocked objects first before calling the original injector.
*/
@Override
public void inject(Object object) {
injectMocks(object);
// The orginalInjector will inject a bean into the fields that are not
// null.
// So, if a mocked object has been injected in the previous step, the
// injector won't touch it.
injectOriginals(object);
}
protected void injectMocks(Object object) {
inject(object, mockedBeansFactory);
}
protected void injectOriginals(Object object) {
if (originalInjector != null) {
originalInjector.inject(object);
}
}
/**
* Called by Wicket when a component is being constructed. Here is the
* opportunity to inject field values into it.
*/
public void onInstantiation(Component component) {
inject(component);
}
}