Mockito @Mock Annotation

December 13, 2020 No comments Mockito Java Framework Testing Mock

1. Introduction

Mockito is one of the most popular mocking frameworks used in Java applications for effective unit testing. The main feature of this framework is defining mocked and spied objects. In this article, we will cover @Mock annotation that is an alternative for the Mockito.mock() method.

2. Enable Mockito Annotations

Mockito annotations in unit tests could be enabled using one of the following ways:

  • by marking test class with @RunWith(MockitoJUnitRunner.class) annotation:

    @RunWith(MockitoJUnitRunner.class)
    public class MockAnnotationUnitTest {
    
      @Mock
      SampleService sampleService;
    }
    
  • by calling the MockitoAnnotations.initMocks() method explicitly in set up method:

    public class MockAnnotationUnitTest {
    
      @Mock
      SampleService sampleService;
    
      @Before
      public void setUp() {
         MockitoAnnotations.initMocks(this);
      }
    }
    
  • by using dedicated MockitoRule:

    public class MockAnnotationUnitTest {
    
      @Rule 
      public MockitoRule rule = MockitoJUnit.rule();
    
      @Mock
      SampleService sampleService;
    }
    

3. Creating mock objects

Let's start with a simple definition of a mock object - it is a dummy implementation for a real interface or a class in which you can define the return values for specific method calls. Mock objects usually are configured in tests to perform a particular behavior.

Mockito provides two methods to create mock objects:

  • using the static Mockito.mock() method,
  • using the @Mock annotation.

To use @Mock, first, we need to enable Mockito annotations - methods to do that were described in point 2.

Mockito can be used in conjunction with the JUnit framework, so our example tests will be based on that library:

package com.frontbackend.libraries.mockito;

import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class MockitoMockTest {

    interface MyDatabaseConnection {
        boolean openConnection();

        int update(String sql);
    }

    class MyService {
        private final MyDatabaseConnection myDatabaseConnection;

        MyService(MyDatabaseConnection myDatabaseConnection) {
            this.myDatabaseConnection = myDatabaseConnection;
            this.myDatabaseConnection.openConnection();
        }

        public boolean updateRows(String query) {
            int updatedRows = myDatabaseConnection.update(query);
            return updatedRows > 0;
        }
    }

    @Mock
    MyDatabaseConnection myDatabaseConnection;

    @Test
    public void shouldSuccessfullyUpdateRows() {
        // Given
        MyService myService = new MyService(myDatabaseConnection);
        when(myDatabaseConnection.update(anyString())).thenReturn(10);

        // When
        boolean success = myService.updateRows("update table set col = 'val'");

        // Then
        verify(myDatabaseConnection, times(1)).openConnection();
        verify(myDatabaseConnection).update("update table set col = 'val'");
        Assert.assertTrue(success);
    }
}

What happens in the above code:

Initialization and configuration:
  • @RunWith(MockitoJUnitRunner.class) - initializing Mockito annotations,
  • @Mock MyDatabaseConnection myDatabaseConnection; - tells Mockito to mock the MyDatabaseConnection class,
  • MyService myService = new MyService(myDatabaseConnection); - instantiates the MyService class using the created mock object (notice that instead of this we could use @InjectMocks annotation),
  • when(myDatabaseConnection.update(anyString())).thenReturn(10); - configure the return value of mocked MyDatabaseConnection object,
Calling the test method:
  • boolean success = myService.updateRows("update table set col = 'val'"); - executing updateRows method from tested object,
Assertions:
  • verify(myDatabaseConnection, times(1)).openConnection(); - checking if openConnection() was called on mocked object once,
  • verify(myDatabaseConnection).update("update table set col = 'val'"); - checking if the update method was called on mocked object,
  • Assert.assertTrue(success) - checking if the called method from test object returned true.

As you can see we can create a mock instance of an object regardless of whether it is an interface or class. It possible to stub method calls to that mock using the Mockito.when(x).thenReturn(y) notation.

4. Configuring mock objects

Mocks can act differently depending on input parameters. To configure the behavior of the methods in mocked objects we could use when(...).then(...) syntax. Methods like anyString(), anyInt(), any(class) could be used to define that depends on the input parameter type a specific value should be returned. The when(...).thenThrow(...) syntax is used to throw an exception when special conditions are met.

Let's extend our test class with a few more tests:

package com.frontbackend.libraries.mockito;

import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class MockitoMockTest {

    interface MyDatabaseConnection {
        boolean openConnection();

        int update(String sql);
    }

    class MyService {
        private final MyDatabaseConnection myDatabaseConnection;

        MyService(MyDatabaseConnection myDatabaseConnection) {
            this.myDatabaseConnection = myDatabaseConnection;
            this.myDatabaseConnection.openConnection();
        }

        public boolean updateRows(String query) {
            int updatedRows = myDatabaseConnection.update(query);
            return updatedRows > 0;
        }
    }

    @Mock
    MyDatabaseConnection myDatabaseConnection;

    @Test
    public void shouldReturnCorrectUpdatedRowsNumber() {
        // Given
        when(myDatabaseConnection.update(anyString())).thenReturn(10);

        // When
        int rows = myDatabaseConnection.update("update all");

        // Then
        Assert.assertEquals(10, rows);
    }

    @Test(expected = RuntimeException.class)
    public void shouldThrowAnException() {
        // Given
        when(myDatabaseConnection.openConnection()).thenThrow(new RuntimeException("Connection cannot be opened!"));
        MyService myService = new MyService(myDatabaseConnection);

        // When
        myService.updateRows("update table set col = 'val'");
    }

    @Test
    public void shouldReturnDifferentValueDependentOnMethodParameter() {
        // Given
        when(myDatabaseConnection.update("update table1")).thenReturn(10);
        when(myDatabaseConnection.update("update table2")).thenReturn(20);
        MyService myService = new MyService(myDatabaseConnection);

        // When
        int rows1 = myDatabaseConnection.update("update table1");
        int rows2 = myDatabaseConnection.update("update table2");

        // Then
        Assert.assertEquals(10, rows1);
        Assert.assertEquals(20, rows2);
    }
}

We can use the verify() method on the mock object to check if the method has been called with the specific parameters and a certain number of times. That information is available because Mockito monitors all the methods calls and their parameters to the mock object.

5. Conclusion

In this article, we presented how to configure mock objects in Mockito tests and verify the behavior of the test class. With the Mockito framework, we can write tests for classes using external systems (like database, filesystem) easily.

As usual code used in this tutorial is available in our GitHub repository.

{{ message }}

{{ 'Comments are closed.' | trans }}