Saturday, March 03, 2007

Mocking with EasyMock

Until now I've been using JMock (currently 1.1) for creating mock objects in my unit tests. Yesterday a decided to give a try to EasyMock (currently 2.2) and ... I must say that I like it very much its approach and will continue to use it, probably switching from JMock (but this I can't say for sure at this point). So I will share some first thoughts about it.

What I liked more about EasyMock is the ease when creating mocks without much hassle. For example I had a test written in this way:

/**
* Check that proxy works when on-demand instantiation creates
* an object which implement the right interface.
*/
public void testProxyWorksOk() {
Mock mockCfgElement = mock( IConfigurationElement.class );

mockCfgElement.expects( once() )
.method( "createExecutableExtension" )
.with( eq( ExtensionsProxyFactory.CLASS_ATTR ) )
.will( returnValue( new ProxyWannabe() ) );

IProxyWannabe proxy = proxyFactory.getProxy(
IProxyWannabe.class,
(IConfigurationElement) mockCfgElement.proxy() );

assertEquals( 0, proxy.getDummy() );
proxy.costlyMethod1();
assertEquals( 1, proxy.getDummy() );
assertNotNull( proxy.costlyMethod2() );
}

This uses the typical JMock-pattern for writing expectations using some kind of specific API for testing (expects( once() ).method( "" ) ...). Typically what you would do is:
  1. Write the expectation(s) for your mock object (what methods are to be called, with which parameters and what values are to be returned).
  2. Get an object implementing the interface you want your object to be tested with.
  3. Run the method you want to test with this mock.
  4. Verify the expectations.
With JMock verification is automatically performed at the end of the method. But the biggest problem here is the passing of the expected method as a string: this is going to give some headache in case of refactoring (something that is going to happen often when acting in a test-driven way). Also the way of passing parameters seems a bit bloated ( with( eq( ... ) )... ).

The same test with EasyMock becomes:

public void testProxyWorksOk() throws Exception {
IConfigurationElement mockCfgElement = createMock( IConfigurationElement.class );

expect( mockCfgElement.createExecutableExtension( ExtensionsProxyFactory.CLASS_ATTR ) )
.andReturn( new ProxyWannabe() );
replay( mockCfgElement );

IProxyWannabe proxy = proxyFactory.getProxy( IProxyWannabe.class, mockCfgElement );

assertEquals( 0, proxy.getDummy() );
proxy.costlyMethod1();
assertEquals( 1, proxy.getDummy() );
assertNotNull( proxy.costlyMethod2() );

verify( mockCfgElement );
}

You will notice:
  • The mock factory method returns an interface compatible with the one we want to mock (IConfigurableElement, in this case): JMock creates a proxy object (instance of type Mock).
  • Writing the expectation is pretty straightforward (and less bloated).
  • Verification must be performed explicitely.
Additionally, you test-cases classes can only inherit from the usual JUnit's TestCase base class while with JMock you must inherit from MockObjectTestCase (not shown here). This is because EasyMock uses static imports from Java 5.0 (and so it can't be used with older JDKs ).

Some resources for the ones who have lived in Outer Space and don't know about mock objects:
Let's see how JMock2 will cope with the challenge ...

No comments: