Monday, 17 November 2008

EasyMock and Spring Autowiring

Spring Framework provides an easy way to unit test components in isolation and EasyMock provides a quick way to create mock objects with very little effort. Combining them together, Spring can be used to create the mock objects so that beans that use auto wiring to inject dependencies can be tested without having to modify the classes or create complex context configuration files.

Creating mock objects using spring is simple, here is a sample beans.xml:
<bean class="org.easymock.EasyMock" factory-method="createNiceMock" primary="true" id="someServiceMock">
   <constructor-arg value="com.example.service.SomeService" />
</bean>
This creates a mock object from the given interface. The primary is used to indicate that it should be used for autowiring which is useful when component scanning is used together with @Component tags. When you want to refer to the actual implementation you would use @Qualifier("someService").

If interfaces aren't used then the following beans.xml is used.
<bean class="org.easymock.classextension.EasyMock" factory-method="createNiceMock" primary="true" id="someDAO" >
   <constructor-arg value="com.example.dao.SomeDAO" />
   <property name="sessionFactory">
      <null />
   </property>
</bean>
Performing the test is relatively simple and with autowiring configuration is minimal. Below I have used the classextension static imports as it can be used for both types of mocks. However the standard expect/replay/etc cannot be used with classextension created mocks.
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.reset;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
import org.junit.Before;
import org.junit.Test;
import org.springframework.ui.ModelMap;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:/controller-beans.xml" })
public class TestSampleController
{
   /** UNIT UNDER TEST */
   @Autowired
   private SampleController controller;
   /** mock object */
   @Autowired
   private SampleService service;

   /**
   * Called before each test.
   */
   @Before
   public void setUp() throws Exception
   {
      reset(service);
   }

   @Test
   public void testPage() throws Exception
   {
      //data
      List data = new LinkedList();

      //prepare
      expect(service.getData()).andReturn(data);
      replay(service);

      //perform
      ModelMap map = new ModelMap();
      String result = controller.page(map);

      //verify
      assertThat("result", result, equalTo("expected");
      assertThat((Map<String,Object>) map, Matchers.hasKey("someobj"));
      assertThat("someobj", map.get("someobj"), notNullValue());
   }
}
Finally there are cases where you need the mock object ready to give a response during load up, such as in an InitializingBean. In those cases the following util class can be adapted:
public class MockUtil
{
   public static <T> T createNiceMock(Class<T> toMock, boolean replay)
   {
      IMocksControl control = EasyMock.createNiceControl();
      T mock = control.createMock(toMock);
      if (replay)
      {
         control.replay();
      }
      return mock;
   }
}
<bean id="dataServiceMock" class="com.example.test.MockUtil"  factory-method="createNiceMock" primary="true">
   <constructor-arg value="com.example.service.DataService" />
   <constructor-arg value="true"/>
</bean>

No comments:

Post a Comment