Mockito Mocking Void Methods

Introduction

Mocking void methods in Mockito can be accomplished using the doNothing(), doThrow(), doAnswer(), doReturn(), and doCallRealMethod() methods. This is particularly useful when you want to control the behavior of a void method, which cannot return a value. This tutorial will demonstrate how to mock void methods in Mockito using different approaches.

Maven Dependencies

To use Mockito with JUnit 5, add the following dependencies to your pom.xml file:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>4.8.1</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-junit-jupiter</artifactId>
    <version>4.8.1</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>5.9.2</version>
    <scope>test</scope>
</dependency>

Example Scenario

We will create a NotificationService class that has a dependency on an EmailService. Our goal is to test the NotificationService methods by mocking the EmailService void methods.

NotificationService and EmailService Classes

First, create the Email class, the EmailService interface, and the NotificationService class.

public class Email {
    private String recipient;
    private String message;

    // Constructor, getters, and setters
    public Email(String recipient, String message) {
        this.recipient = recipient;
        this.message = message;
    }

    public String getRecipient() {
        return recipient;
    }

    public void setRecipient(String recipient) {
        this.recipient = recipient;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

public interface EmailService {
    void sendEmail(Email email);
}

public class NotificationService {
    private final EmailService emailService;

    public NotificationService(EmailService emailService) {
        this.emailService = emailService;
    }

    public void sendNotification(String recipient, String message) {
        Email email = new Email(recipient, message);
        emailService.sendEmail(email);
    }
}

JUnit 5 Test Class with Mockito

Create a test class for NotificationService using JUnit 5 and Mockito.

import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.junit.jupiter.api.extension.ExtendWith;

@ExtendWith(MockitoExtension.class)
public class NotificationServiceTest {

    @Mock
    private EmailService emailService;

    @InjectMocks
    private NotificationService notificationService;

    @Test
    public void testSendNotificationWithDoNothing() {
        // Given
        String recipient = "[email protected]";
        String message = "Hello, World!";

        // Mock void method
        doNothing().when(emailService).sendEmail(any(Email.class));

        // When
        notificationService.sendNotification(recipient, message);

        // Then
        verify(emailService, times(1)).sendEmail(any(Email.class));
    }

    @Test
    public void testSendNotificationWithDoThrow() {
        // Given
        String recipient = "[email protected]";
        String message = "Hello, World!";

        // Mock void method to throw an exception
        doThrow(new RuntimeException("Email service is down")).when(emailService).sendEmail(any(Email.class));

        // When & Then
        RuntimeException exception = assertThrows(RuntimeException.class, () -> {
            notificationService.sendNotification(recipient, message);
        });
        assertEquals("Email service is down", exception.getMessage());
    }

    @Test
    public void testSendNotificationWithDoAnswer() {
        // Given
        String recipient = "[email protected]";
        String message = "Hello, World!";

        // Mock void method with custom behavior
        doAnswer(invocation -> {
            Email email = invocation.getArgument(0);
            assertEquals("[email protected]", email.getRecipient());
            assertEquals("Hello, World!", email.getMessage());
            return null;
        }).when(emailService).sendEmail(any(Email.class));

        // When
        notificationService.sendNotification(recipient, message);

        // Then
        verify(emailService, times(1)).sendEmail(any(Email.class));
    }

    @Test
    public void testSendNotificationWithDoCallRealMethod() {
        // Given
        String recipient = "[email protected]";
        String message = "Hello, World!";

        // Mock void method to call real method
        doCallRealMethod().when(emailService).sendEmail(any(Email.class));

        // Here you would need an implementation of the sendEmail method in EmailService
        // For this example, assume it's a real implementation that you want to test

        // When
        notificationService.sendNotification(recipient, message);

        // Then
        verify(emailService, times(1)).sendEmail(any(Email.class));
    }
}

Explanation

  1. Creating Mocks with @Mock:

    • The @Mock annotation creates a mock instance of the EmailService interface. This mock instance can be used to simulate the behavior of the EmailService in a controlled way.
  2. Injecting Mocks with @InjectMocks:

    • The @InjectMocks annotation injects the mock EmailService into the NotificationService instance to provide a controlled test environment. This allows the NotificationService methods to be tested in isolation from the actual EmailService implementation.
  3. Mocking Void Methods:

    • doNothing(): The doNothing().when(emailService).sendEmail(any(Email.class)); method configures the mock EmailService to do nothing when the sendEmail method is called with any Email object.
    • doThrow(): The doThrow(new RuntimeException("Email service is down")).when(emailService).sendEmail(any(Email.class)); method configures the mock EmailService to throw a RuntimeException when the sendEmail method is called with any Email object.
    • doAnswer(): The doAnswer(invocation -> { ... }).when(emailService).sendEmail(any(Email.class)); method configures the mock EmailService to execute custom behavior when the sendEmail method is called with any Email object.
    • doCallRealMethod(): The doCallRealMethod().when(emailService).sendEmail(any(Email.class)); method configures the mock EmailService to call the real method implementation when the sendEmail method is called with any Email object.

Additional Scenarios

Scenario: Mocking Void Methods with Custom Behavior and Call Count

In this scenario, we will demonstrate how to mock a void method with custom behavior and verify the call count using ArgumentMatchers.

@Test
public void testSendNotificationWithCustomBehaviorAndCallCount() {
    // Given
    String recipient = "[email protected]";
    String message = "Hello, World!";

    // Mock void method with custom behavior
    doAnswer(invocation -> {
        Email email = invocation.getArgument(0);
        assertEquals("[email protected]", email.getRecipient());
        assertEquals("Hello, World!", email.getMessage());
        return null;
    }).when(emailService).sendEmail(any(Email.class));

    // When
    notificationService.sendNotification(recipient, message);
    notificationService.sendNotification(recipient, message);

    // Then
    verify(emailService, times(2)).sendEmail(any(Email.class));
}

Explanation

  1. Mocking Void Method with Custom Behavior and Call Count:
    • The doAnswer(invocation -> { ... }).when(emailService).sendEmail(any(Email.class)); method configures the mock EmailService to execute custom behavior when the sendEmail method is called with any Email object.
    • The verify(emailService, times(2)).sendEmail(any(Email.class)); method checks if the sendEmail method was called on the EmailService exactly twice with any Email object.

Conclusion

Mocking void methods in Mockito simplifies the configuration of method calls on mock objects for unit testing. By using doNothing(), doThrow(), doAnswer(), and doCallRealMethod(), you can handle various scenarios and control the behavior of void methods. This step-by-step guide demonstrated how to effectively mock void methods in your unit tests, covering different scenarios to ensure comprehensive testing of the NotificationService class.

Comments