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 AddressService 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: " + expectedId, 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();
  }
}

…which i hope you’ll agree is more progressional than my quick attempt at writing a mock.

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.

Reference: Unit Testing Using Mocks – Testing Techniques 5 from our JCG partner at the Captain Debug blog

Related Articles :

Related Whitepaper:

Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions

Get ready to program in a whole new way!

Functional Programming in Java will help you quickly get on top of the new, essential Java 8 language features and the functional style that will change and improve your code. This short, targeted book will help you make the paradigm shift from the old imperative way to a less error-prone, more elegant, and concise coding style that’s also a breeze to parallelize. You’ll explore the syntax and semantics of lambda expressions, method and constructor references, and functional interfaces. You’ll design and write applications better using the new standards in Java 8 and the JDK.

Get it Now!  

Leave a Reply


× two = 2



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

20,709 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books