Java Testing
🔥

Java Testing

Mockito

Init

Enable Mockito annotations

  • Call the method MockitoAnnotations.initMocks(this) to initialize annotated fields
  • Use the built-in runner @RunWith(MockitoJUnitRunner.class)

Mock

Default to return null value
Java
// MyList listMock = mock(MyList.class, "myMock");
User user = Mockito.mock(User.class);
when(user.getName()).thenReturn("John");
doReturn(true).when(user).getName());
 
Java
//org.mockito.Mockito.mock(java.lang.Class<T>)
//org.mockito.Mockito.mock(java.lang.Class<T>, java.lang.String)
//org.mockito.Mockito.mock(java.lang.Class<T>, org.mockito.stubbing.Answer)
//org.mockito.Mockito.mock(java.lang.Class<T>, org.mockito.MockSettings)

//method 1
Foo foo1 = Mockito.mock(Foo.class);
//method 2:define mock name
Foo foo2 = Mockito.mock(Foo.class, "mock_1");
//method 3:mock logic
Foo foo3 = Mockito.mock(Foo.class, new Answer() {
            @Override
            public Object answer(InvocationOnMock invocation) throws Throwable {
                //define return value
                return null;
            }
});
//method 4:use MockSettings configure mock
Foo foo4 = Mockito.mock(Foo.class, Mockito.withSettings().defaultAnswer(Mockito.RETURNS_SMART_NULLS));

/* 
Mockito.withSettings().useConstructor().defaultAnswer(CALLS_REAL_METHODS)

Mockito.withSettings()
    .useConstructor("arg1", 123).defaultAnswer(CALLS_REAL_METHODS)

InnerAbstract spy = mock(InnerAbstract.class, Mockito.withSettings().useConstructor().outerInstance(outerInstance).defaultAnswer(CALLS_REAL_METHODS));
*/

Spy - to spy on an existing instance

Default to execute original methods
Java
User user = Mockito.spy(new User());
when(user.getName()).thenReturn("John");
doReturn(true).when(user).getName());
 
Java
//org.mockito.Mockito.spy(java.lang.Class<T>)
//org.mockito.Mockito.spy(T)

//method 1:spy a class
List spy1 = Mockito.spy(List.class);
//method 2:spy an object
List spy2 = Mockito.spy(new ArrayList<>());
 
Java
@ExtendWith(MockitoExtension.class)
public class GreetingsTest {
    // ...
}

Mock Inject

create and inject mocked instances
Java
@ExtendWith(MockitoExtension.class)
public class MockitoTest {

    //Inject it with attributes having @Mock annotations
    @InjectMocks
    private Foo foo;

    //will be used to inject to foo
    @Mock
    private Bar bar;

    @Test
    public void mockTest() {
        Mockito.when(bar.add(1, 2)).thenReturn(7);

        int result = foo.sum(1, 2);

        Assert.assertEquals(7, result);
    }
}
 

Configure method to call the real method:

Java
when(listMock.size()).thenCallRealMethod();
assertThat(listMock).hasSize(1);

MockException

Java
// way two
when(listMock.add(anyString()))
  .thenReturn(false)
  .thenThrow(IllegalStateException.class);

// way one
doThrow(NullPointerException.class).when(listMock).clear();

Invocation times

Java
// Verify number of interactions
verify(mockedList, times(1)).size();

// Verify no interaction with the whole mock
verifyNoInteractions(mockedList);

// Verify no interaction with a specific method
verify(mockedList, times(0)).size(); //atLeast(1), atMost(1)
verify(mockedList, never()).size();

// Verify order of interactions
InOrder inOrder = Mockito.inOrder(mockedList);
inOrder.verify(mockedList).size();
inOrder.verify(mockedList).add("a parameter");
inOrder.verify(mockedList).clear();

// Verify interaction with exact argument
verify(mockedList).add("test");

verify(flowerService).analyze(or(eq("poppy"), endsWith("y")));

@Captor

Java
@Mock
List mockedList;

@Captor 
ArgumentCaptor argCaptor;

@Test
public void whenUseCaptorAnnotation_thenTheSame() {
    mockedList.add("one");
    Mockito.verify(mockedList).add(argCaptor.capture());

    assertEquals("one", argCaptor.getValue());
}
 
Java
anyInt()
anyString()

DoThrow

Java
@Test(expected = Exception.class)
public void givenNull_addThrows() {
    MyList myList = mock(MyList.class);
    doThrow().when(myList).add(isA(Integer.class), isNull());
 
    myList.add(0, null);
}

doNothing

Java
@Test
public void whenAddCalledValueCaptured() {
    MyList myList = mock(MyList.class);
    ArgumentCaptor<String> valueCapture = ArgumentCaptor.forClass(String.class);
    doNothing().when(myList).add(any(Integer.class), valueCapture.capture());
    myList.add(0, "captured");
 
    assertEquals("captured", valueCapture.getValue());
}

Answering

Java
@Test
public void whenAddCalledAnswered() {
    MyList myList = mock(MyList.class);
    doAnswer(invocation -> {
        Object arg0 = invocation.getArgument(0);
        Object arg1 = invocation.getArgument(1);
        
        assertEquals(3, arg0);
        assertEquals("answer me", arg1);
        return null;
    }).when(myList).add(any(Integer.class), any(String.class));
    myList.add(3, "answer me");
}

Verify

has been called and call times
Java
@Test
public void verify_publicMethodReturnString() {
    verify(mockSample, never()).publicMethodReturnString();

    mockSample.publicMethodReturnString();

    verify(mockSample).publicMethodReturnString();
    mockSample.publicMethodReturnString();
    verify(mockSample, times(2)).publicMethodReturnString();
}
isA、anyXxx、eq
Java
@Test
public void verify_publicMethodCalculate() {
    verify(mockSample, never()).publicMethodCalculate(1, 2);


    verify(mockSample, never()).publicMethodCalculate(isA(int.class), isA(int.class));
    verify(mockSample, never()).publicMethodCalculate(anyInt(), anyInt());
    verify(mockSample, never()).publicMethodCalculate(eq(1), eq(2));

    mockSample.publicMethodCalculate(1, 2);
    verify(mockSample).publicMethodCalculate(1, 2);
    verify(mockSample).publicMethodCalculate(isA(int.class), isA(int.class));
    verify(mockSample).publicMethodCalculate(anyInt(), anyInt());
    verify(mockSample).publicMethodCalculate(eq(1), eq(2));
    verify(mockSample, never()).publicMethodCalculate(1, 1);
    verify(mockSample, never()).publicMethodCalculate(eq(1), eq(1));
}
 
notion image
 
The following table gives an overview of the most important annotations in JUnit 5 from the org.junit.jupiter.api package.
Annotation
Description
@Test
Identifies a method as a test method.
@Disabled("reason")
Disables a test method with an option reason.
@BeforeEach
Executed before each test. Used to prepare the test environment, e.g., initialize the fields in the test class, configure the environment, etc.
@AfterEach
Executed after each test. Used to cleanup the test environment, e.g., delete temporary data, restore defaults, cleanup expensive memory structures.
@DisplayName("<Name>")
<Name> that will be displayed by the test runner. In contrast to method names the name can contain spaces to improve readability.
@RepeatedTest(<Number>)
Similar to @Test but repeats the test a <Number> of times
@BeforeAll
Annotates a static method which is executed once, before the start of all tests. It is used to perform time intensive activities, for example, to connect to a database. Methods marked with this annotation need to be defined as static to work with JUnit.
@AfterAll
Annotates a static method which is executed once, after all tests have been finished. It is used to perform clean-up activities, for example, to disconnect from a database. Methods annotated with this annotation need to be defined as static to work with JUnit.
@TestFactory
Annotates a method which is a Factory for creating dynamic tests
@Nested
Lets you nest inner test classes to group your tests and to have additional @BeforeEach and @AfterEach methods.
@Tag("<TagName>")
Tags a test method, tests in JUnit 5 can be filtered by tag. E.g., run only tests tagged with "fast".
@ExtendWith
Lets you register an Extension class that adds functionality to the tests

Assert throw

Java
assertThatThrownBy(() -> verify(listMock, times(2)).add(anyString()))
    .isInstanceOf(TooFewActualInvocations.class)
    .hasMessageContaining("myMock.add");