Thursday, 24 November 2011

Unit Testing Using Mocks - Testing Techniques 5

My last blog was the fourth in a series of blogs on approaches to testing code, demonstrating how to create a unit test that isolates the object under test using a stub object. Today’s blog looks at what is sometimes regarded as an opposing technique: unit testing with mock objects. Again, I’m using my simple scenario of retrieving an address from a database:


… and testing the AddressService1 class:

@Component
public class AddressService {

 
private static final Logger logger = LoggerFactory.getLogger(AddressService.class);

 
private AddressDao addressDao;

 
/**
   * Given an id, retrieve an address. Apply phony business rules.
   *
   *
@param id
   *            The id of the address object.
   */
 
public Address findAddress(int id) {

   
logger.info("In Address Service with id: " + id);
    Address address = addressDao.findAddress
(id);

    address = businessMethod
(address);

    logger.info
("Leaving Address Service with id: " + id);
   
return address;
 
}

 
private Address businessMethod(Address address) {

   
logger.info("in business method");

   
// Apply the Special Case Pattern (See MartinFowler.com)
   
if (isNull(address)) {
     
address = Address.INVALID_ADDRESS;
   
}

   
// Do some jiggery-pokery here....

   
return address;
 
}

 
private boolean isNull(Object obj) {
   
return obj == null;
 
}

 
@Autowired
  @Qualifier
("addressDao")
 
void setAddressDao(AddressDao addressDao) {
   
this.addressDao = addressDao;
 
}
}

...by replacing he data access object with a mock object.

Before continuing, it would be a good idea to define what exactly a mock object is and how it differs from a stub. If you read my last blog, you’ll remember that I let Martin Fowler define a stub object as:

“Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what's programmed in for the test.”

...which is taken from his essay Mocks Aren’t Stubs.

So, how do mock object differ to stubs? When you hear people talk about mock objects, they often mention that they’re mocking behaviour or mocking roles, but what does that mean? The answer lies in the way a unit test and a mock object work together to test your object. The mock object scenario goes like this:
  1. A mock object is defined in the test.
  2. The mock object is injected into your object under test
  3. The test specifies which methods on the mock object will be called, plus the arguments and return values. This is known as ‘setting expectations’.
  4. The test then runs.
  5. The test then asks the mock to verify that all the method calls specified in step three were called correctly. If they were then the test passes. If they weren’t then the test fails.
Therefore, mocking behaviour or mocking roles really means checking that your object under test calls methods on a mock object correctly and failing the test if it doesn’t; hence, you’re asserting on the correctness of method calls and the execution path through your code, rather than, in the case of a regular unit test, the return value of the method under test.

Although there are several professional mocking frameworks available, for this example I first decided to produce my own AddressDao mock, which fulfils the above requirements. After all, how hard can it be?

public class HomeMadeMockDao implements AddressDao {

 
/** The return value for the findAddress method */
 
private Address expectedReturn;

 
/** The expected arg value for the findAddress method */
 
private int expectedId;

 
/** The actual arg value passed in when the test runs */
 
private int actualId;

 
/** used to verify that the findAddress method has been called */
 
private boolean called;

 
/**
   * Set and expectation: the return value for the findAddress method
   */
 
public void setExpectationReturnValue(Address expectedReturn) {
   
this.expectedReturn = expectedReturn;
 
}

 
public void setExpectationInputArg(int expectedId) {
   
this.expectedId = expectedId;
 
}

 
/**
   * Verify that the expectations have been met
   */
 
public void verify() {

   
assertTrue(called);
    assertEquals
("Invalid arg. Expected: " + expectedId + " actual: " + actualId, expectedId, actualId);
 
}

 
/**
   * The mock method - this is what we're mocking.
   *
   *
@see com.captaindebug.address.AddressDao#findAddress(int)
   */
 
@Override
 
public Address findAddress(int id) {

   
called = true;
    actualId = id;
   
return expectedReturn;
 
}
}

The unit test code that supports this mock is:

public class MockingAddressServiceWithHomeMadeMockTest {

 
/** The object to test */
 
private AddressService instance;

 
/**
   * We've written a mock,,,
   */
 
private HomeMadeMockDao mockDao;

 
@Before
 
public void setUp() throws Exception {
   
/* Create the object to test and the mock */
   
instance = new AddressService();
    mockDao =
new HomeMadeMockDao();
   
/* Inject the mock dependency */
   
instance.setAddressDao(mockDao);
 
}

 
/**
   * Test method for
   *
{@link com.captaindebug.address.AddressService#findAddress(int)}.
   */
 
@Test
 
public void testFindAddressWithEasyMock() {

   
/* Setup the test data - stuff that's specific to this test */
   
final int id = 1;
    Address expectedAddress =
new Address(id, "15 My Street", "My Town", "POSTCODE", "My Country");

   
/* Set the Mock Expectations */
   
mockDao.setExpectationInputArg(id);
    mockDao.setExpectationReturnValue
(expectedAddress);

   
/* Run the test */
   
instance.findAddress(id);

   
/* Verify that the mock's expectations were met */
   
mockDao.verify();
 
}
}

Okay, although this demonstrates the steps required to carry out a unit test using a mock object, it’s fairly rough and ready, and very specific to the AddressDao/AddressService scenario. To prove that it’s already been done better, the following example uses easyMock as a mocking framework. The unit test code in this more professional case is:

@RunWith(UnitilsJUnit4TestClassRunner.class)
public class MockingAddressServiceWithEasyMockTest {

 
/** The object to test */
 
private AddressService instance;

 
/**
   * EasyMock creates the mock object
   */
 
@Mock
 
private AddressDao mockDao;

 
/**
   *
@throws java.lang.Exception
   */
 
@Before
 
public void setUp() throws Exception {
   
/* Create the object to test */
   
instance = new AddressService();
 
}

 
/**
   * Test method for
   *
{@link com.captaindebug.address.AddressService#findAddress(int)}.
   */
 
@Test
 
public void testFindAddressWithEasyMock() {

   
/* Inject the mock dependency */
   
instance.setAddressDao(mockDao);
   
/* Setup the test data - stuff that's specific to this test */
   
final int id = 1;
    Address expectedAddress =
new Address(id, "15 My Street", "My Town", "POSTCODE", "My Country");
   
/* Set the expectations */
   
expect(mockDao.findAddress(id)).andReturn(expectedAddress);
    replay
();

   
/* Run the test */
   
instance.findAddress(id);

   
/* Verify that the mock's expectations were met */
   
verify();
 
}
}

The main criticism levelled at using mock objects is that they closely couple the unit test code to the implementation of the production code. This is because the code that sets the expectations closely tracks the execution path of the production code. This means that subsequent refactoring of the production code can break a multitude of tests even though the class still fulfills its interface contract. This give rise to the assertion that mock tests are fairly brittle and that you’ll spend time fixing them unnecessarily, which from experience I agree with - although using ‘non-strict’ mocks, which don’t care about the order in which methods expectations are called, alleviates the problem to a degree.

On the other hand, once you know how to use a framework like easyMock, producing unit tests that isolate you object under test can be done very quickly and efficiently.

In self critiquing this example code, I’d like to point out that I think that using a mock object is overkill in this scenario, plus, you could also easily argue that I’m using a mock as a stub.

Several years ago, when I first came across easyMock, I used mocks everywhere, but recently I’ve come to prefer manually writing stubs for application boundary classes, such as DAOs, and objects that merely return data. This is because stub based tests are arguably a lot less brittle than mock based tests especially when all you need to is access data.

Why use mocks? Mocks good at testing an application written using the ‘tell don’t ask’ technique, to verify that a method with a void return is called.



A list of Blogs on Testing Techniques


1The source code is available from GitHub at:

git://github.com/roghughe/captaindebug.git

5 comments:

Anonymous said...

Great series of posts on unit testing! Your blog rocks. By the way, what software do you use for your diagrams?

Roger Hughes said...

Thanks.

I've drawn the diagrams using Visual Paradigm UML Community Edition.

Anonymous said...

There seems to be a small gotcha in your verify() method :

it should be assertEquals("Invalid arg. Expected: " + expectedId + " actual: " + actualId, expectedId, actualId);

Cheers

Roger Hughes said...

Whoops. Thanks for that. The code has now been corrected.

11 plus mock tests said...

I am Very thank full the owner of this blog. Because of this blog is very informative for me.And I ask u some thing You make more this type blog where we can get more knowledge