2011-02-03

Mockito Recipes

Last year I met a very interesting framework : Mockito.

Mockito is a mock framework for Java. It helps the developer to create mock objects for unit testing.  If you are new to mocking, I suggest you to read Martin Fowler's "Mocks Aren't Stubs".

There are many mock frameworks in Java. I tried JMock before, however I found that Mockito was easier to use. With JMock you must specify all the invocations that occurs on the mock object. If you don't do it, your test will fail. Mockito is more permissive in this way. With Mockito, if you really need to verify an invocation, you just specify it.

Of course, if you practice Test Driven Development, I think it’s better to use JMock’s style. But it’s good to know that you can use Mockito in the JMock’s way, by telling Mockito to verify all the invocations. For this reason, I think Mockito is a more versatile framework for mocking objects.

When you verify the interactions with a mock object, you make sure that it doesn’t make unplanned interactions with an unrelated object. There's an interesting post on the subject by J.B. Rainsberger on his blog.

However, I don't want to spark a debate on mocking techniques, the goal of this post is to get you started with Mockito.

So to start with Mockito :

Add JUnit’s and Mockito’s static imports. It helps the readability of the code.

import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;

Now you are ready to do some mocking!

Here are some Mockito recipes :

List<String> list = mock(List.class);

list.add("Test");
list.clear();

// verification
verify(list, times(1)).add("Test");
verify(list, times(1)).clear();

list.add("Test");
list.clear();
list.contains("Test");

//in order verification
InOrder inOrder = inOrder(list);
inOrder.verify(list).add("Test");
inOrder.verify(list).clear();
try {
// verify that there is no more interaction with the object
 verifyNoMoreInteractions(list);
 fail();
} catch (NoInteractionsWanted e) {
 // Expected
}

// thenThrow pattern
when(list.add(eq("Test"))).thenThrow(
  new RuntimeException("Major failure"));
try {
 list.add("Test");
 fail();
} catch (Exception e) {
 // Expected
}

// thenReturn pattern + chaining
when(list.contains(anyObject())).thenReturn(false).thenReturn(true);
assertFalse(list.contains("Test")); // first thenReturn
assertTrue(list.contains("Test")); // second thenReturn

// thenAnswer pattern
when(list.contains(any(String.class))).thenAnswer(
  new Answer<Boolean>() {
   @Override
   public Boolean answer(InvocationOnMock invocation_)
     throws Throwable {
    // do some stuff here (during the invocation,

    // you could play with the invocation parameters for
    // example)

    return ((String) invocation_.getArguments()[0])
      .startsWith("T");
   }
  });

assertTrue(list.contains("Test"));
assertFalse(list.contains("Best"));

// this boolean is used only for showing
// that the    code in the     answer object
// is really invocated
final AtomicBoolean invocated = new AtomicBoolean();

// mock of a void method call
doAnswer(new Answer<Object>() {
 @Override
 public Object answer(InvocationOnMock invocation_) throws
Throwable {
  // do something useful if it make sense :)
  invocated.set(true);
  return null;
 }
}).when(list).clear();

assertFalse(invocated.get());
list.clear();
// assert that the code in the Answer has been executed.
assertTrue(invocated.get());


// spy on an instance
// lets make a dummy class.
class TestSpy {
 private String _name;

 public void setName(String name_) {
  _name = name_;
 }

 public String getName() {
  return _name;
 }
}
;
// spy on it.
// (be sure to read the javadoc of the spy method prior using it)
TestSpy t = spy(new TestSpy());
t.setName("will");
assertEquals("will", t.getName());

// change the behavior of the getName() method
when(t.getName()).thenReturn("bob");
assertEquals("bob", t.getName());


Happy mocking!

5 comments:

  1. I've just read Martin Fowler's article. Thanks for sharing it!

    Also, Wikipedia have an extensive list of mock object frameworks for many languages.

    ReplyDelete
  2. Hey Tony, how are you?? Look. if i want to pass a return list for some mocked object, like:

    List listaRetorno = new Arraylist();
    Something retorno1 = new Something();
    retorno1.setCodRetorno(1);
    listaRetorno.add(retorno1);
    Something retorno2 = new Something();
    retorno1.setCodRetorno(1);

    when(this.lancamentoDelegateMock.gravarLancamentosIntegracao(new ArrayList())).
    thenReturn(listaRetorno);


    It should return on the production code the list that i created and mounted, but it always returns an empty list for me. Do you know why?

    ReplyDelete
    Replies
    1. Hi Kalil,

      Without seeing all the objects ( the mock declaration, the interfaces you are trying to mock) it's difficult to pinpoint exactly what is not working.

      However, are you sure the parameter is passed correctly in the code that is playing with the mock?

      I suspect that your method "gravarLancamentosIntegracao" is called with a null. Check this because it could be a potential NPE when you run the code in production.

      If you don't care about the null parameter, You can try with anyList() instead of new ArrayList() in your when() statement.

      Take a look at this code. It works and it shows the difference.

      https://gist.github.com/codingtony/b2d02a994136fe2fa353

      You can copy it in a unit test and play with it.

      I hope it will help

      -tony


      Delete
    2. Hi Tony,

      Nice work, I just gone through the github code, in there you are simply using the arrayList object in thenReturn but I cant it gives compile time error can not resolve method thenReturn(java.Util.List),
      Do you have any idea why it would be


      My code is something like this,

      @Test
      public void test(){
      when(helper.getCompanies(company).thenReturn(getCompanyList()))
      }

      public List getCompanyList()
      {

      Company c1 = new Company();
      c1.setName("ABC");

      Company c2 = new Company();
      c2.setName("XYZ");

      List companyList = new ArrayList();
      companyList.add(c1);
      companyList.add(c2);

      return companyList;
      }

      Delete
  3. Hello,
    What a post :) Great work. I will use your ideas in future. Thanks for sharing. Keep it up.

    ReplyDelete