Java - How to write test code using Mockito

Mockito is a popular mocking framework in Java. You can write unit tests by mocking objects with Mockito.

You can create Mock objects yourself, but using a mocking framework like Mockito saves you from writing cumbersome code.

In this article, we will see how to write test code with Mockito.

  • Mocking
  • Verify
  • ArgumentCaptor
  • Spying
  • Exception

set dependencies

In your gradle project you can use JUnit and Mockito library by setting the dependencies like this:

dependencies {
    testImplementation 'junit:junit:4.12'
    testImplementation 'org.mockito:mockito-core:2.7.22'
}

Mocking

You can create a Mock object with mock(ClassToMock.class) like this:

When a mock object is created, all the code inside does not work, and the return value is replaced with 0, false, null, etc.

@Test
public void mockingList() {
    List mockList = mock(ArrayList.class);

    mockList.add("apple");
    assertEquals("apple", mockList.get(0));
}

When the above code is executed, null is returned and the test fails.

java.lang.AssertionError:
Expected :apple
Actual   :null

When

You can use when to make the Mock object return a certain value under certain circumstances. This is called stubbing.

Interpreting the following code is that when a mockList object calls get(0), it should return "appple".

when(mockList.get(0)).thenReturn("apple");

As follows, a specific value is set when get(), size() is called.

@Test
public void mockingList_when() {
    List mockList = mock(ArrayList.class);

    when(mockList.get(0)).thenReturn("apple");
    when(mockList.get(1)).thenReturn("kiwi");
    when(mockList.size()).thenReturn(10);

    assertEquals("apple", mockList.get(0));
    assertEquals("kiwi", mockList.get(1));
    assertEquals(10, mockList.size());
}

If you want to return a specific value regardless of the input argument, you can use anyInt(). In addition, methods for all types such as anyString() and anyLong() are provided.

@Test
public void mockingList_when2() {
    List mockList = mock(ArrayList.class);

    when(mockList.get(anyInt())).thenReturn("apple");
    assertEquals("apple", mockList.get(0));
    assertEquals("apple", mockList.get(1));
}

Verify

Using verify, you can check which API was called on the mock object and how many times it was called.

The following code checks if mockList.add("apple") is called. If not called, the test will fail.

verify(mockList).add("apple");

The following code checks if mockList.get(0) is called twice. Pass the expected number of calls to the argument of times().

verify(mockList, times(2)).get(0);

So you can check if the method is called like this: By providing methods such as atLeastOnce(), atLeast(), and times(), you can check how many times it has been called.

@Test
public void mockingList_verify() {
    List mockList = mock(ArrayList.class);

    mockList.add("apple");
    verify(mockList).add("apple");

    mockList.get(0);
    mockList.get(0);
    verify(mockList, times(2)).get(0);
    verify(mockList, atLeast(2)).get(0);
}

ArgumentCaptor

ArgumentCaptor is used to check the argument passed to the Mock object.

The following code reads the argument of mockList.add(). You can pass capture() to verify and check what value was passed to getValue().

ArgumentCaptor<String> arg = ArgumentCaptor.forClass(String.class);
verify(mockList).add(arg.capture());
assertEquals("apple", arg.getValue());

You can check which arguments are passed to add() using ArgumentCaptor as follows.

@Test
public void captorExample() {
    ArgumentCaptor<String> arg = ArgumentCaptor.forClass(String.class);

    mockList.add("apple");
    verify(mockList).add(arg.capture());

    assertEquals("apple", arg.getValue());
}

Spying

It was said that the internal implementation of the mock object does not work and 0, false, null, etc. are returned. The Spy object behaves like a real object, and you can verify which method was called with verify. You can also stubbing some methods with the when keyword.

You can create a spy object like this: If you run it, you can see that the test passes and behaves like a real object.

@Test
public void spyingList() {
    List spyList = spy(ArrayList.class);

    spyList.add("apple");
    assertEquals("apple", spyList.get(0));
}

You can verify that the method was called with verify like this:

@Test
public void spyingList_verify() {
    List spyList = spy(ArrayList.class);

    spyList.add("apple");
    verify(spyList).add("apple");

    spyList.get(0);
    verify(spyList).get(0);
}

Also, you can stubbing some methods with when like this: But the code below has a problem.

@Test
public void spyingList_when_error() {
    List spyList = spy(ArrayList.class);

    when(spyList.get(0)).thenReturn("apple");
    when(spyList.get(1)).thenReturn("kiwi");
    when(spyList.size()).thenReturn(10);

    assertEquals("apple", spyList.get(0));
    assertEquals("kiwi", spyList.get(1));
    assertEquals(10, spyList.size());
}

When I run the above code, the test fails with an exception IndexOutOfBoundsException. Since the spy object behaves like a real object, an Exception may occur because there is no Item when calling spyList.get(0) in the spyList.get(0) code.

 java.lang.IndexOutOfBoundsException: Index: 0, Size: 0

 	at java.util.ArrayList.rangeCheck(ArrayList.java:659)
 	at java.util.ArrayList.get(ArrayList.java:435)

In this case, you can successfully stubbing by changing the pattern to doReturn().when().

@Test
public void spyingList_when_ok() {
    List spyList = spy(ArrayList.class);

    doReturn("apple").when(spyList).get(0);
    doReturn("kiwi").when(spyList).get(1);
    doReturn(10).when(spyList).size();

    assertEquals("apple", spyList.get(0));
    assertEquals("kiwi", spyList.get(1));
    assertEquals(10, spyList.size());
}

So, do not use the when().thenReturn() pattern when stubbing a spy object. I recommend using the doReturn().when() pattern.

Exception

You can also cause an Exception to be thrown under certain circumstances. You can do this using the when().thenThrow(ExceptionClass) pattern.

@Test
public void whenGetUserInfo_throwException() {
    Contacts contactsMock= mock(Contacts.class);
    when(contactsMock.getUserInfo(anyInt()))
            .thenThrow(NullPointerException.class);

    contactsMock.getUserInfo(0);
}

You can also create and pass an Exception object like new NullPointerException(msg) without passing the class name as an argument to new NullPointerException(msg).

@Test
public void whenGetUserInfo_throwExceptionWithMsg() {
    Contacts contactsMock= mock(Contacts.class);
    when(contactsMock.getUserInfo(anyInt()))
            .thenThrow(new NullPointerException("Null pointer exception happened"));

    contactsMock.getUserInfo(0);
}

When the above codes are executed, an Exception is raised and the test fails. Since it is confirmed that an Exception is raised, the test should pass.

As follows, if a NullPointerException does not occur, the test will fail, and if an Exception is raised, it can be implemented to succeed.

@Test
public void whenGetUserInfo_throwException2() {
    Contacts contactsMock= mock(Contacts.class);
    when(contactsMock.getUserInfo(anyInt()))
            .thenThrow(NullPointerException.class);

    try {
        contactsMock.getUserInfo(0);
        fail();
    } catch (NullPointerException e) {
        // pass
    }
}

A simpler and more readable way is to specify in @Test that an exception will be raised: If an Exception occurs, the test succeeds, otherwise the test fails.

@Test(expected = NullPointerException.class)
public void whenGetUserInfo_throwException3() {
    Contacts contactsMock= mock(Contacts.class);
    when(contactsMock.getUserInfo(anyInt()))
            .thenThrow(NullPointerException.class);

    contactsMock.getUserInfo(0);
}

Throwing an exception in the Void-Return Type method

The method set to void should be implemented to throw an exception with the doThrow().when() pattern as follows.

@Test(expected = NullPointerException.class)
public void whenCallsVoidReturnTypeMethod_throwException() {
    Contacts contactsMock= mock(Contacts.class);
    doThrow(NullPointerException.class)
            .when(contactsMock)
            .addUserInfo(anyString(), anyString());

    contactsMock.addUserInfo("AAA", "123-456");
}

throw exception on spy object

Spy objects can also throw exceptions in the same way as mocks:

@Test(expected = NullPointerException.class)
public void whenGetUserInfo_throwException4() {
    Contacts contactsMock= spy(Contacts.class);
    when(contactsMock.getUserInfo(anyInt()))
            .thenThrow(NullPointerException.class);

    contactsMock.getUserInfo(0);
}

Clean up

We learned how to create Mock and Spy objects using Mockito library and verify them with verify. We also saw how to stubbing with when to make it return a specific value or raise an exception.

codechachaCopyright ©2019 codechacha