How to use Mockito to supercharge your Java Testing

Mockito is a powerful and easy-to-use mocking framework for Java applications. With Mockito, you can easily create mock objects that simulate the behavior of real objects in your application, without actually invoking their methods. We will show at first how to plug Mockito in JUnit Tests. Then, we will integrate Mockito in Quarkus Tests.

Mockito provides several key features that make it a popular choice for testing Java applications, including:

  • Simple and intuitive API for creating and configuring mock objects
  • Support for mocking interfaces, abstract classes, and concrete classes
  • Ability to verify method invocations and interactions between objects
  • Support for stubbing method calls to return specific values or throw exceptions
  • Integration with popular testing frameworks like JUnit and TestNG

In this tutorial, we’ll explore the key features of the Mockito framework and demonstrate how to use them to create effective unit tests for your Java applications.

Creating tests with Mockito

To use Mockito in your tests, you first need to add it as a dependency to your project. You can do this by adding the following lines to your pom.xml file:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>3.8.0</version>
    <scope>test</scope>
</dependency>

Then, you can create mock objects using the Mockito.mock() method. For example, if you want to create a mock object of the Department class, you can do so like this:

Department departmentMock = Mockito.mock(Department.class);

This creates a mock object that behaves like an instance of the Department class, but doesn’t actually call its methods. To configure the behavior of the mock object, you can use Mockito’s API to specify which methods should be mocked and what values they should return. For example:

Mockito.when(departmentMock.getName()).thenReturn("Engineering");

This configures the getName() method of the departmentMock object to return the string “Engineering” when it’s called. You can also configure the mock object to throw an exception, or to perform some other custom behavior.

Once you’ve created your mock objects, you can use them in your tests. Here is the full test:

@Test
void testDepartmentName() {
    Department departmentMock = Mockito.mock(Department.class);
    Mockito.when(departmentMock.getName()).thenReturn("Engineering");

    String actualName = departmentMock.getName();
    assertEquals("Engineering", actualName);
}

Using InjectMocks in your tests

Mockito also provides an @InjectMocks annotation, which is used to inject mock objects into a target object. This is useful when you want to test a specific component of your application, but you need to mock some of its dependencies.

To use @InjectMocks, you first need to create the target object that you want to test. For example, if you wanted to test a DepartmentService class that depends on a DepartmentRepository, you could create the DepartmentService object like this:

public class DepartmentService {
    private DepartmentRepository departmentRepository;

    public DepartmentService(DepartmentRepository departmentRepository) {
    this.departmentRepository = departmentRepository;
    }

    public List<Department> getAllDepartments() {
    return departmentRepository.getAll();
    }
}

DepartmentRepository departmentRepositoryMock = Mockito.mock(DepartmentRepository.class);
DepartmentService departmentService = new DepartmentService(departmentRepositoryMock);

This creates a mock object of the DepartmentRepository class, and then creates a DepartmentService object that depends on that mock object.

Next, you can use the @InjectMocks annotation to inject the mock object into the departmentService object. For example:

@InjectMocks
private DepartmentService departmentService;

This tells Mockito to inject the departmentRepositoryMock object into the departmentService object, so that when you call departmentService.getAllDepartments(), it will use the mock object instead of the real DepartmentRepository.

Testing indirect interactions to verify the logical paths of your application

Mockito provides several ways to verify that certain methods are called on your mock objects during a test. One of the most common is the Mockito.verify() method, which you can use to check that a specific method was called with specific arguments.

For example, let’s say you have a method in your application that takes a Department object as an argument, and then calls another method on that object:

public void doSomethingWithDepartment(Department department) {
    department.update();
}

You could test this method using a mock object like this:

Department departmentMock = Mockito.mock(Department.class);
doSomethingWithDepartment(departmentMock);
Mockito.verify(departmentMock).update();

This code creates a mock object of the Department class, calls the doSomethingWithDepartment() method with the mock object as an argument, and then verifies that the update() method was called on the mock object.

You can also use the Mockito.verify() method to check that a method was called a certain number of times, or to check that it was never called at all. This can be useful for testing complex logical paths in your application, to make sure that all the necessary methods are called in the right order.

Using Mockito with Quarkus

The Mockito framework fits very well into Quarkus testing. As a matter of fact, you can use a specific Quarkus dependency to bind Mock objects in a QuarkusTest Class:

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-junit5-mockito</artifactId>
    <scope>test</scope>
</dependency>

For example, consider the following QuarkusTest Class:

@QuarkusTest
public class ShopServiceTest {

    @Test
    public void testExample() {
    ShopService shopService = Mockito.mock(ShopService.class);
    UUID uuid = UUID.randomUUID();

    Mockito.when(shopService.getOrder(1L)).thenReturn(new Order(uuid, "Customer Name", 123));
    Mockito.when(shopService.getOrder(null)).thenThrow(new NullPointerException());

    Order order1 = shopService.getOrder(uuid);
    Order order2 = null;

    // Assert that order1 is not null
    assertNotNull(order1);

    // Assert that order1 has the expected UUID, customer name, and total
    assertEquals(uuid, order1.getUuid());
    assertEquals("Customer Name", order1.getCustomerName());
    assertEquals(123, order1.getTotal());

    // Assert that a NullPointerException is thrown when trying to get an order with null ID
    assertThrows(NullPointerException.class, () -> {
        order2 = shopService.getOrder(null);
    });
    }
}

In this example, we are creating a Mock of the ShopService. Then, we add some logic through the Mockito.when methods. As a result, subsequent invocations of these methods will have a mock response. Finally, we are verifying that the methods return the correct Order objects or null with a set of assertEquals and assertThrows methods.

Mocking Panache and Rest Clients

The Mockito framework greatly simplifies Integration tests. Some examples include calling Rest Clients or Persistence framework such as Panache.
For example, here is how you can create a Mock for a RestClient to simulate the response from a Service:

@InjectMock
@RestClient
UserService userService; 

@Test
public void testExample() {

 String userJson = "{\"id\":123,\"name\":\"John Doe\",\"email\":\"[email protected]\",\"gender\":\"male\"";

    Mockito.when(userService.single()).thenReturn(userJson);

    given()
      .when().get("/users/single")
      .then()
         .statusCode(200)
         .body(is(userJson));
}

In the above code, the @RestClient UserService is decorated with @InjectMock. Therefore, we will be able to use a stub for the UserService, in combination with the Mockito methods.

The Mockito.when clause intercepts the single() method from the UserService:

@Path("/users")
@RegisterRestClient(configKey = "users-api")
public interface UserService {

    @GET
    @Path("/single")
    @Produces(MediaType.TEXT_PLAIN)
    public String single();


}

Besides the RestClient, you can also use Mockito with Panache Entity and Repository classes. If you need to mock Panache entities using the active record pattern, then you have to add the following extension in your project.

<dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-panache-mock</artifactId>
        <scope>test</scope>
</dependency>

The above extensions allows to use PanacheMock on your PanacheEntity classes to invoke its static methods.

Here is an example code shows how to mock the Department PanacheEntity and the DepartmentRepository which is a PanacheRepository:

@QuarkusTest
public class DepartmentEndpointTest {

    @BeforeAll
    public static void setup() {

    PanacheMock.mock(Department.class);
    }
    @InjectMock
    DepartmentRepository departmentRepository;

    @Test
    public void testCustomerService() {


    Mockito
            .when(Department.listAll())
            .thenReturn(Collections.singletonList(new Department(1l, "Department X", "codeX")));

    given()
            .when().get("/departments")
            .then()
            .statusCode(200)
            .body("$.size()", is(1));

    Mockito
            .when(departmentRepository.findById(2l))
            .thenReturn(new Department(2l, "Department Y", "codeY"));

    Department department = departmentRepository.findById(2l);
    assertAll("department",
            () -> assertEquals("Department Y", department.name),
            () -> assertEquals("codeY", department.code)
    );

    Mockito.verify(departmentRepository, Mockito.times(1)).findById(2l);

    }
}

The PanacheMock.mock, which is in a @BeforeAll method, creates a mock of the Department class for every test of the class. Any call to the entity methods use the mocked class. Then we are showing three types of usages for Mockito:

  • At first, we create a stud for the listAll() method of the DepartmentEntity. A RESTAssured check on the /departments endpoint size follows
  • Then, we create a stub on the findById method of the Repository Class. We verify with an assertAll statement the fields of the Department
  • Finally, we do a logical verification that the findById(2l) execution was triggered only 1 time.

To continue learning how to Test Quarkus applications, we recommend this article: How to Test Quarkus applications

Conclusion

In this tutorial, we’ve explored the key features of the Mockito Framework, including creating and configuring mock objects, using @Mock and @InjectMocks annotations, and verifying method invocations. Mockito is a powerful and flexible tool that can help you write more effective and reliable tests for your Java applications. By using Mockito, you can isolate and test individual components of your application in a controlled and predictable way, leading to better quality and more maintainable code.

Found the article helpful? if so please follow us on Socials