Mock WebClient object with Answers.RETURNS_DEEP_STUBS cannot stub .header() & .headers() in mockito mockito
Explanation of the problem
When unit testing by mocking the WebClient of Spring Boot WebFlux with @Mock(answer = Answers.RETURNS_DEEP_STUBS), an issue was discovered where the return value was null when calling the .header() and .headers() methods.
Dependencies used were Zulu OpenJDK 17.0.2, spring-boot-starter-webflux:2.6.7, mockito-core:4.0.0, and mockito-junit-jupiter:4.0.0.
The error message “Cannot invoke “org.springframework.web.reactive.function.client.WebClient$RequestHeadersSpec.retrieve()” because the return value of “org.springframework.web.reactive.function.client.WebClient$RequestHeadersSpec.header(String, String[])” is null” was displayed.
The code contained a @Service class named WebClientService, which contained two records, WebClientRequest and WebClientResponse. The WebClientService class used a WebClient instance and a WebClient Builder instance to make a request that returned a ResponseEntity containing an object of WebClientResponse. The request made a GET call to http://localhost:1234, which included the header “a”:”a”. Commenting out this line resolved the issue.
The tests were written using Mockito, MockitoExtension, and InjectMocks annotations. The WebClient and WebClient Builder instances were mocked using the @Mock(answer = Answers.RETURNS_DEEP_STUBS) and @Mock(answer = Answers.RETURNS_SELF) annotations, respectively. The tests included a single test method named test() that called webClientService.request() and asserted that the HttpStatus of the response was HttpStatus.OK.
Troubleshooting with the Lightrun Developer Observability Platform
Getting a sense of what’s actually happening inside a live application is a frustrating experience, one that relies mostly on querying and observing whatever logs were written during development.
Lightrun is a Developer Observability Platform, allowing developers to add telemetry to live applications in real-time, on-demand, and right from the IDE.
- Instantly add logs to, set metrics in, and take snapshots of live applications
- Insights delivered straight to your IDE or CLI
- Works where you do: dev, QA, staging, CI/CD, and production
Start for free today
Problem solution for DRAFTMock WebClient object with Answers.RETURNS_DEEP_STUBS cannot stub .header() & .headers() in mockito mockito
If you are using Mockito
and having trouble stubbing the .header()
and .headers()
methods on a mock WebClient
object that has been created with Answers.RETURNS_DEEP_STUBS
, it could be because these methods are not implemented in the Mockito
version you are using.
One solution to this problem is to avoid using Answers.RETURNS_DEEP_STUBS
and instead use a more specific stubbing approach, such as doReturn()
. Here is an example of how you can stub the .header()
method:
WebClient.RequestHeadersUriSpec<?> headersUriSpec = Mockito.mock(WebClient.RequestHeadersUriSpec.class);
Mockito.doReturn(headersUriSpec).when(webClient).get();
WebClient.RequestHeadersSpec<?> headersSpec = Mockito.mock(WebClient.RequestHeadersSpec.class);
Mockito.doReturn(headersSpec).when(headersUriSpec).uri(Mockito.anyString());
Mockito.doReturn(headersSpec).when(headersSpec).header(Mockito.anyString(), Mockito.anyString());
Here, we first create a mock RequestHeadersUriSpec
object and use doReturn()
to return it when webClient.get()
is called. We then create a mock RequestHeadersSpec
object and use doReturn()
to return it when headersUriSpec.uri()
is called. Finally, we use doReturn()
again to return the same headersSpec
object when headersSpec.header()
is called.
You can follow a similar approach to stub the .headers()
method.
Other popular problems with in mockito mockito
Problem: Stubbing Private Methods
One common issue with Mockito is that it cannot directly stub private methods. This can be problematic when you have a private method that you want to stub for your test case. The reason behind this limitation is that Mockito relies on dynamic subclassing to create a proxy object for the mocked object, and since private methods cannot be accessed by subclasses, they cannot be mocked by Mockito.
Solution:
One solution to this problem is to use PowerMock, a testing framework that extends Mockito and provides the ability to mock private methods.
Problem: Testing Static Methods
Another common problem with Mockito is that it cannot mock static methods. This can be a limitation when you are trying to test a class that contains static methods, as the behavior of the static method cannot be easily controlled by the test case.
Solution:
One solution to this problem is to use a testing framework like PowerMock or JMockit, which provide the ability to mock static methods. Another solution is to refactor your code to remove the static methods and use instance methods instead, which can be more easily mocked with Mockito.
Problem: Verifying Order of Method Calls
A third issue with Mockito is that it can be difficult to verify the order in which methods were called on a mocked object. This can be important when you want to ensure that a method was called after another method, for example.
Solution:
One solution to this problem is to use the InOrder object provided by Mockito, which allows you to verify the order in which methods were called. Another solution is to use the ArgumentCaptor object, which can be used to capture the arguments passed to a method and then verify the order in which the arguments were captured. Finally, you can also use a combination of the verify and times methods to ensure that methods were called in a specific order and a specific number of times.
A brief introduction to in mockito mockito
Mockito is a popular open-source testing framework for Java that allows developers to easily create mock objects and verify interactions between them. It provides a simple and intuitive API for creating mock objects, defining their behavior, and verifying that they were used correctly in tests. Mockito can be used with any testing framework, such as JUnit or TestNG, and it supports both unit testing and integration testing.
One of the key features of Mockito is its ability to create mock objects that behave as if they were real objects, allowing developers to isolate and test individual components of their code. Mockito allows developers to specify the behavior of mock objects using simple method calls, such as when() and thenReturn(), and it provides a rich set of verification methods to ensure that mock objects are used correctly. Mockito also provides advanced features, such as argument matchers and the ability to verify that methods were called in a specific order, to make testing more flexible and powerful. Overall, Mockito is a powerful and flexible tool for testing Java code that has gained widespread adoption in the industry.
Most popular use cases for in mockito mockito
- Testing behavior of methods: Mockito is widely used to test the behavior of methods in isolation from other parts of the application. It allows developers to create mock objects of dependencies that are required for a particular method to execute, and then set expectations on the mock objects to verify the behavior of the method under test. For example, you can use Mockito to verify that a method calls a particular method on a dependency with the correct arguments.
// Example of using Mockito to verify behavior of a method
@Test
public void testSomeMethod() {
Dependency dependencyMock = Mockito.mock(Dependency.class);
MyClass myClass = new MyClass(dependencyMock);
// Set expectation on mock object
Mockito.when(dependencyMock.someMethod(Mockito.anyString())).thenReturn("mockedResult");
// Call method under test
myClass.someMethod();
// Verify behavior
Mockito.verify(dependencyMock).someMethod("expectedArgument");
}
- Stubbing method responses: Mockito can also be used to stub method responses of dependencies that are required for a method to execute. This is particularly useful when the behavior of the dependency is not important for the test, and we only need it to return a specific value. For example, you can use Mockito to stub a database call to always return a particular result, to avoid hitting the actual database during testing.
// Example of using Mockito to stub method responses
@Test
public void testSomeMethod() {
Dependency dependencyMock = Mockito.mock(Dependency.class);
MyClass myClass = new MyClass(dependencyMock);
// Stub method response
Mockito.when(dependencyMock.someMethod(Mockito.anyString())).thenReturn("mockedResult");
// Call method under test
String result = myClass.someMethod();
// Verify result
assertEquals("expectedResult", result);
}
- Verifying interactions between objects: Mockito can also be used to verify interactions between objects. This is particularly useful when we want to ensure that a method call on a dependency was made during the execution of a method. For example, you can use Mockito to verify that a method on a database object was called during the execution of a method.
// Example of using Mockito to verify interactions between objects
@Test
public void testSomeMethod() {
Dependency dependencyMock = Mockito.mock(Dependency.class);
MyClass myClass = new MyClass(dependencyMock);
// Call method under test
myClass.someMethod();
// Verify interaction
Mockito.verify(dependencyMock).someMethod("expectedArgument");
}
It’s Really not that Complicated.
You can actually understand what’s going on inside your live applications.