Home Arrow Icon Knowledge base Arrow Icon Global Arrow Icon How do I simulate API failures using Mockito


How do I simulate API failures using Mockito


Simulating API failures using Mockito is a crucial aspect of unit testing, allowing you to verify how your application handles errors without relying on actual external failures. Here's a detailed guide on how to achieve this:

Understanding Mockito Basics

Mockito is a popular mocking framework for Java that allows you to create mock objects and define their behavior. This is particularly useful for isolating dependencies and simulating various scenarios, including failures.

Simulating API Failures

To simulate API failures, you can use Mockito to throw exceptions when a method is called. There are two primary methods for this:

1. `when().thenThrow()`: This is used for non-void methods. You can specify an exception to be thrown when a method is called.

2. `doThrow().when()`: This is used for void methods. It allows you to throw an exception when a void method is invoked.

Example: Simulating API Failure with `when().thenThrow()`

Let's say you have a method `fetchData()` in a class `ApiService` that you want to mock to throw an exception.

java
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
public class ApiServiceTest {

    @Mock
    private ApiService apiService;

    @Test
    void testApiFailure() {
        // Mock the apiService to throw an exception when fetchData() is called
        when(apiService.fetchData()).thenThrow(new RuntimeException("API failed"));

        // Assert that calling fetchData() throws the expected exception
        assertThrows(RuntimeException.class, () -> {
            apiService.fetchData();
        });
    }
}

Example: Simulating API Failure with `doThrow().when()`

If `fetchData()` is a void method, you would use `doThrow().when()` instead:

java
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.doThrow;

@ExtendWith(MockitoExtension.class)
public class ApiServiceTest {

    @Mock
    private ApiService apiService;

    @Test
    void testApiFailureVoidMethod() {
        // Mock the apiService to throw an exception when fetchData() is called
        doThrow(new RuntimeException("API failed")).when(apiService).fetchData();

        // Assert that calling fetchData() throws the expected exception
        assertThrows(RuntimeException.class, () -> {
            apiService.fetchData();
        });
    }
}

Simulating Multiple Failures and Success

If you want to simulate a scenario where the API fails once and succeeds on the second call, you can chain multiple behaviors using `thenReturn()` or `thenThrow()` methods. However, for chaining exceptions, you would typically use `thenThrow()` followed by another `thenThrow()` or `thenReturn()` for success.

java
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
public class ApiServiceTest {

    @Mock
    private ApiService apiService;

    @Test
    void testApiFailureThenSuccess() {
        // Mock the apiService to throw an exception on the first call and return success on the second
        when(apiService.fetchData())
                .thenThrow(new RuntimeException("API failed"))
                .thenReturn("Success");

        // Assert that the first call throws an exception
        assertThrows(RuntimeException.class, () -> {
            apiService.fetchData();
        });

        // Assert that the second call returns success
        String result = apiService.fetchData();
        assert result.equals("Success");
    }
}

Best Practices

- Be Specific: Only mock the behavior that is relevant to your test. Avoid over-mocking to keep tests focused.
- Handle Executions Properly: Ensure that your application code is prepared to handle exceptions correctly.
- Use Descriptive Error Messages: Include meaningful messages when mocking exceptions to help diagnose issues.
- Test Exception Handling Logic: Verify that your code handles exceptions as expected.

By following these guidelines, you can effectively simulate API failures using Mockito and ensure that your application is robust and resilient against various error scenarios.

Citations:
[1] https://stackoverflow.com/questions/11785498/simulate-first-call-fails-second-call-succeeds
[2] https://stackoverflow.com/questions/11785498/simulate-first-call-fails-second-call-succeeds/41272746
[3] https://www.browserstack.com/guide/mockito-throw-exception
[4] https://www.vogella.com/tutorials/Mockito/article.html
[5] https://wiremock.org/docs/simulating-faults/
[6] https://stackoverflow.com/questions/46817155/how-do-i-get-mockito-mocks-to-cause-a-failure-when-unexpected-calls-are-made
[7] https://intellij-support.jetbrains.com/hc/en-us/community/posts/10002813476754-IntelliJ-IDEA-2022-2-4-errors-when-running-Mockito-tests
[8] https://keploy.io/blog/community/mastering-mocking-a-complete-guide-to-mocks-and-other-test-doubles