Would you start mocking me?

August 5th, 2010 by David Kessler Leave a reply »

One of the primary principles of unit testing is to test a small piece of functionality in isolation. In order to achieve this, mock objects are often necessary. Historically using mocks could be quite painful. After using several mock frameworks, my favorite by far is Mockito.

Tutorial

In this tutorial we will walk through examples of the most common features of Mockito. My sample project can be downloaded here.

Interfaces and Implementation

Some mocking frameworks only supported mocking interfaces. As a result our projects became bloated with useless interfaces that were only used for testing.  Mockito creates mock objects with interfaces or classes.

interface Person{
   List getChildren();
   Boolean isCitizen();
   Integer getAge();
   Map getSchedule();
   void haveChild();
}
class PersonImpl implements Person{
   public List getChildren() { return null; }
   public Boolean isCitizen() { return null; }
   public Integer getAge() { return null; }
   public Map getSchedule() { return null; }
   public void haveChild() { }
}
assertNotNull(Mockito.mock(Person.class));
assertNotNull(Mockito.mock(PersonImpl.class));

Default Values

Mockito provides stub implementations for all of the methods on the interface/class. It also provides several common sense defaults out of the box.

Person mockPerson = Mockito.mock(PersonImpl.class);
assertNotNull(mockPerson.getChildren());
assertEquals(0, mockPerson.getChildren().size());
assertFalse(mockPerson.isCitizen());
assertEquals(new Integer(0), mockPerson.getAge());
assertNotNull(mockPerson.getSchedule());

Mockito returns empty Lists and Maps to prevent NPE’s in your test code. It defaults to false for Boolean return values. By default Zero is returned for Integer, Long and Double. Notice that even though PersonImpl returns null for all of its methods the Mockito version returns reasonable default values.

Changing Behavior

Person mockPerson = mock(PersonImpl.class);
Mockito.when(mockPerson.getAge()).thenReturn(35);
assertEquals(new Integer(35), mockPerson.getAge());

Mockito.when() takes a mock and a method call that returns a value. Mockito’s thenReturn() method takes the return value that Mockito will provide to all matching calls. By default the method getAge() returns Zero, within Mockito, but now it returns 35. This ability to control behavior is the key to testing functionality in isolation.

Verifying Method Calls

Person mockPerson = Mockito.mock(PersonImpl.class);
mockPerson.getAge();
Mockito.verify(mockPerson).getAge();

Mockito.verify() takes a mock object and verifies that the following method is called.

Person mockPerson = Mockito.mock(PersonImpl.class);
mockPerson.getAge();
mockPerson.getAge();
Mockito.verify(mockPerson, Mockito.times(2)).getAge();

This is the same verify as before except a second parameter is passed to the verify() method telling it how many times the trailing method should be called.

Person mockPerson = Mockito.mock(PersonImpl.class);
mockPerson.getAge();
mockPerson.getAge();
Mockito.verify(mockPerson, Mockito.atLeastOnce()).getAge();

Sometimes it is not important how many times a method is called as long as it is called at least once. Mockito.atLeastOnce() method provides this flexibility.

No More Interactions

Person mockPerson = Mockito.mock(PersonImpl.class);
mockPerson.getAge();
Mockito.verify(mockPerson).getAge();
Mockito.verifyNoMoreInteractions(mockPerson);

Mockito.verifyNoMoreInteractions() ensures that no other methods have been called on the PersonImpl class. Since Mockito is very helpful in stubbing out all of the methods for a class or interface there is no control over which methods have been called. Only methods that have been explicitly verified are checked for specific calls.

Throw Exception

@Test(expected = RuntimeException.class)
public void testThenThrow(){
   Person mockPerson = Mockito.mock(PersonImpl.class);
   Mockito.when(mockPerson.getAge()).thenThrow(new RuntimeException("test exception"));
   mockPerson.getAge();
}

The thenThrow() method prepares the method defined in when() to throw an Exception. While I am not attempting to provide a tutorial on JUnit4 features, I feel it necessary to point out the Annotation verifies that a RuntimeException is thrown in the test.

@Test(expected = RuntimeException.class)
   public void testDoThrow(){
   Person mockPerson = Mockito.mock(PersonImpl.class);
   Mockito.doThrow(new RuntimeException("test exception")).when(mockPerson).haveChild();
   mockPerson.haveChild();
}

The doThrow() syntax is necessary to throw an Exception for a void method. The previous example threw an exception for Person.getAge() which is not a void method. In this example Person.haveChild() is void and requires this alternate syntax in order to compile.

TDD with Mockito

Let’s drive a small class that we will call Bouncer.  This class will check if a Person is old enough to drink.  First let’s write a test.

@Test
public void testCheckAgeOf_UnderAge(){
   Person mockPerson = Mockito.mock(PersonImpl.class);
   Mockito.when(mockPerson.getAge()).thenReturn(20);
   Bouncer bouncer = new Bouncer();
   assertFalse(bouncer.checkAgeOf(mockPerson));
}

Of coarse this does not even compile. We need to create a Bouncer class with the checkAgeOf() method. And here is the amazing class

class Bouncer{
   public boolean checkAgeOf(Person customer){
      return false;
   }
}

So why did I return false. Well the test does not require anything more than this to pass. TDD is about doing the simplest thing that works and then refactor the code. This is the simplest thing that works. Now let’s write another test.

@Test
public void testCheckAgeOf_OfAge(){
   Person mockPerson = Mockito.mock(PersonImpl.class);
   Mockito.when(mockPerson.getAge()).thenReturn(21);
   Bouncer bouncer = new Bouncer();
   assertTrue(bouncer.checkAgeOf(mockPerson));
}

Now we need real business logic in our method.

return customer.getAge() >= 21;

Green again. Now it is time to refactor. In this simple case I am not sure how to simplify the code. There are a few more tests that should be written. A test if getAge() returns null and if Person is null and other boundary cases. Since the focus of this article is on Mockito I will stop here.

Mockito is a powerful mocking framework that simplifies the creation and usage of mock objects. I encourage you to download my sample code and continue to explore the Mockito framework.

Advertisement

3 comments

  1. Jim Chrystal says:

    Thanks, Sud. I was going to ask you about this before you moved to your next contract. Good stuff!

  2. Sudhakar Ramasamy says:

    Here’s another awesome post that delves into a few things we need to watch out for when using any mocking framework.

    http://hamletdarcy.blogspot.com/2010/09/mockito-pros-cons-and-best-practices.html

  3. Glenn Guden says:

    thanks!…
    the explanations were very understandable.

Leave a Reply

*