This article is about fixing DRAFTMock WebClient object with Answers.RETURNS_DEEP_STUBS cannot stub .header() & .headers() in mockito mockito
  • 19-Feb-2023
Lightrun Team
Author Lightrun Team
Share
This article is about fixing DRAFTMock WebClient object with Answers.RETURNS_DEEP_STUBS cannot stub .header() & .headers() in mockito mockito

Mock WebClient object with Answers.RETURNS_DEEP_STUBS cannot stub .header() & .headers() in mockito mockito

Lightrun Team
Lightrun Team
19-Feb-2023

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

  1. 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");
}
  1. 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);
}
  1. 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");
}
Share

It’s Really not that Complicated.

You can actually understand what’s going on inside your live applications.

Try Lightrun’s Playground

Lets Talk!

Looking for more information about Lightrun and debugging?
We’d love to hear from you!
Drop us a line and we’ll get back to you shortly.

By submitting this form, I agree to Lightrun’s Privacy Policy and Terms of Use.