Core Java

Mocking an Enum Using Mockito

When writing unit tests in Java, there are scenarios where we need to mock certain classes or behaviors to isolate and test specific functionality. While mocking objects in Java is straightforward with the help of Mockito, things can get tricky when we need to mock enums due to their final and static nature. However, with the right approach, Mockito provides solutions to mock enums effectively.
Let us delve into understanding how Java Mockito mocking enum works and examine different methods for achieving reliable tests.

1. Introduction

In Java, enums are special data types that represent a group of constants (unchangeable variables). While enums are useful in many scenarios, they can pose challenges when writing unit tests, especially when you need to mock them for testing purposes. Normally, enums are not easily mockable with traditional mocking frameworks like Mockito, as enums are final and their values are static. This can create a situation where testing code that relies on enum values becomes difficult. However, with the right approach, you can mock enums in your tests.

2. Getting started

To get started with mocking enums using Mockito, you need to set up the following dependencies in your project. You can use Maven or Gradle to add these dependencies. If you’re using Maven, add the following dependency:

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

If you’re using Gradle, add:

testImplementation 'org.mockito:mockito-core:latest_jar_version'

3. Example Setup

Let’s take an example of an enum called OrderStatus that has values such as PENDING, SHIPPED, and DELIVERED.

public enum OrderStatus {
    PENDING,
    SHIPPED,
    DELIVERED;
}

Suppose we have a service class OrderService that depends on this enum:

public class OrderService {
    public boolean isOrderShipped(OrderStatus status) {
        return status == OrderStatus.SHIPPED;
    }
}

4. Solution With and without Mocking an Enum

4.1 Without Mocking

Without mocking the enum, the test look like this:

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

public class OrderServiceTest {

    @Test
    public void testIsOrderShipped() {
        OrderService service = new OrderService();
        assertTrue(service.isOrderShipped(OrderStatus.SHIPPED));
        assertFalse(service.isOrderShipped(OrderStatus.PENDING));
    }
}

4.1.1 Code Explanation

In this test, the focus is on verifying the behavior of the OrderService class, specifically the isOrderShipped method. The method takes an enum value of OrderStatus, which represents the status of an order, and checks whether the order has been shipped.

First, an instance of the OrderService class is created using OrderService service = new OrderService();. Then, the assertTrue method is called to verify that when the OrderStatus.SHIPPED value is passed to the isOrderShipped method, the result is true. This ensures that the order is correctly marked as shipped when the status is SHIPPED.

The second assertion uses assertFalse to verify that when the OrderStatus.PENDING value is passed to the isOrderShipped method, the result is false. This ensures that an order with a “pending” status is correctly identified as not having been shipped.

Overall, this test verifies that the isOrderShipped method works as expected by checking if it returns the correct boolean values based on the OrderStatus enum values.

4.1.2 Code Output

This test checks the actual behavior of the isOrderShipped method without any mocking.

  • When service.isOrderShipped(OrderStatus.SHIPPED) is called, it should return true, so assertTrue will pass.
  • When service.isOrderShipped(OrderStatus.PENDING) is called, it should return false, so assertFalse will pass.

Since both assertions are expected to pass, the output will be:

Test passed: testIsOrderShipped

If the isOrderShipped method didn’t return true for OrderStatus.SHIPPED or false for OrderStatus.PENDING, the test would fail, showing an assertion error like:

AssertionError: expected true but was false

4.2 With Mocking

To mock the enum, we can use Mockito and the MockedStatic feature.

import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import static org.mockito.Mockito.*;

public class OrderServiceTest {

    @Test
    public void testIsOrderShippedWithMockedEnum() {
        try (MockedStatic<OrderStatus> mockedEnum = mockStatic(OrderStatus.class)) {
            mockedEnum.when(() -> OrderStatus.SHIPPED).thenReturn(OrderStatus.PENDING); // Mocking enum behavior
            
            OrderService service = new OrderService();
            assertFalse(service.isOrderShipped(OrderStatus.SHIPPED));  // Mocked to PENDING
        }
    }
}

4.2.1 Code Explanation

In this test, the goal is to mock the behavior of the OrderStatus enum. The OrderStatus enum is typically used to represent different states of an order, such as PENDING, SHIPPED, and DELIVERED. The test mocks the OrderStatus.SHIPPED value to simulate a scenario where it behaves as if it is PENDING instead.

The code uses MockedStatic to mock the static method behavior of the OrderStatus enum. The mockStatic method is used within a try-with-resources statement to automatically clean up after the mock is used. Inside the mock block, mockedEnum.when(() -> OrderStatus.SHIPPED).thenReturn(OrderStatus.PENDING) defines the behavior of the mocked enum value. Specifically, when the OrderStatus.SHIPPED value is accessed, it is replaced with OrderStatus.PENDING.

Next, an instance of the OrderService class is created, and the isOrderShipped method is called with the mocked OrderStatus.SHIPPED value. Since the value is mocked to return PENDING, the method assertFalse is used to check that the result is false, indicating that the order is not considered “shipped” according to the mocked behavior.

This test verifies that the OrderService class behaves correctly when the OrderStatus.SHIPPED enum value is replaced with PENDING, showcasing how to mock enums using Mockito.

4.2.1 Code Output

In this test, OrderStatus.SHIPPED is mocked to behave as if it is OrderStatus.PENDING. Therefore, when service.isOrderShipped(OrderStatus.SHIPPED) is called, the result should be false. Since assertFalse expects false, the test will pass successfully. The output will be:

Test passed: testIsOrderShippedWithMockedEnum

If there were any issues with the mock or if isOrderShipped(OrderStatus.SHIPPED) returned
true, the test would fail, giving an error like:

AssertionError: expected false but was true

5. Conclusion

Mocking enums in Java with Mockito is possible, though it requires a more advanced technique using MockedStatic. While enums are typically difficult to mock due to their static and final nature, Mockito provides an elegant solution to handle such cases for unit testing.

Remember that mocking enums should be done judiciously, as it can sometimes lead to brittle tests. However, in scenarios where you need to simulate different behaviors of an enum, mocking can be a useful tool.

Yatin Batra

An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button