Reputation: 45
I am currently testing a Service performing HTTP requests with WebClient
on Spring. When I'm mocking the service I get a NullPointerException
on my body when I'm testing a method which performs a POST request. Here is the error :
java.lang.NullPointerException: Cannot invoke "org.springframework.web.reactive.function.client.WebClient$RequestHeadersSpec.retrieve()" because the return value of "org.springframework.web.reactive.function.client.WebClient$RequestBodySpec.body(org.reactivestreams.Publisher, java.lang.Class)" is null
Here is my code :
import com.bluelagoon.payetonkawa.dolibarr.entities.input.LoginEntity;
import com.bluelagoon.payetonkawa.dolibarr.entities.output.SuccessEntity;
import com.bluelagoon.payetonkawa.dolibarr.entities.output.UserInfoEntity;
import com.bluelagoon.payetonkawa.dolibarr.services.DolibarrInfraService;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
@Service
public class DolibarrInfraServiceImpl implements DolibarrInfraService {
private final WebClient webClient;
public DolibarrInfraServiceImpl(WebClient webClient) {
this.webClient = webClient;
}
@Override
public SuccessEntity login(LoginEntity login) {
return webClient.post()
.uri("/login")
.body(Mono.just(login), LoginEntity.class)
.retrieve()
.bodyToMono(SuccessEntity.class)
.block();
}
...
}
Here is my Test class :
package com.bluelagoon.payetonkawa.dolibarr.impl;
import com.bluelagoon.payetonkawa.dolibarr.entities.input.LoginEntity;
import com.bluelagoon.payetonkawa.dolibarr.entities.output.SuccessEntity;
import com.bluelagoon.payetonkawa.dolibarr.entities.output.TokenEntity;
import com.bluelagoon.payetonkawa.dolibarr.services.DolibarrInfraService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentMatchers;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import java.net.http.HttpRequest;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.lenient;
@ExtendWith(MockitoExtension.class)
class DolibarrInfraServiceImplTest {
@Mock
private WebClient webClientMock;
@Mock
private WebClient.RequestBodyUriSpec requestBodyUriSpecMock;
@Mock
private WebClient.RequestBodySpec requestBodySpecMock;
@Mock
private WebClient.RequestHeadersUriSpec requestHeadersUriSpecMock;
@Mock
private WebClient.RequestHeadersSpec requestHeadersSpecMock;
@Mock
private WebClient.ResponseSpec responseSpecMock;
@InjectMocks
private DolibarrInfraServiceImpl dolibarrInfraService;
@Test
void loginTest_should_return_a_valid_SuccessEntity(){
var loginEntity = new LoginEntity();
loginEntity.setLogin("test");
loginEntity.setPassword("test");
var tokenEntity = new TokenEntity();
tokenEntity.setCode(200);
tokenEntity.setToken("test");
var successEntity = new SuccessEntity();
successEntity.setSuccess(tokenEntity);
when(webClientMock.post())
.thenReturn(requestBodyUriSpecMock);
when(requestBodyUriSpecMock.uri(anyString()))
.thenReturn(requestBodySpecMock);
when(requestBodySpecMock.body(Mono.just(loginEntity), LoginEntity.class))
.thenReturn(requestHeadersSpecMock);
when(requestHeadersSpecMock.retrieve())
.thenReturn(responseSpecMock);
when(responseSpecMock.bodyToMono(ArgumentMatchers.<Class<SuccessEntity>>notNull()))
.thenReturn(Mono.just(successEntity));
var expectedTokenEntity = new TokenEntity();
expectedTokenEntity.setToken("test");
expectedTokenEntity.setCode(200);
var expectedSuccessEntity = new SuccessEntity();
expectedSuccessEntity.setSuccess(expectedTokenEntity);
var resultSuccessEntity = dolibarrInfraService.login(loginEntity);
assertEquals(expectedSuccessEntity, resultSuccessEntity);
}
}
Am I missing something?
Thank you for your answers
Upvotes: 0
Views: 9940
Reputation: 2284
For those who stumble upon this thread and finding hard time to mock any downstream calls using webClient, I hope this will help in someway.
Actual code which calls the downstream:
public Mono<String> getSomething(final MyContext myContext) {
return myCustomWebClient
.get()
.uri("/my/downstream")
.headers(headers -> {
headers.setBearerAuth(myContext.jwtToken());
headers.set("header1", myContext.getHeader1());
})
.retrieve()
.bodyToMono(String.class));
}
Now here is my TestClass to test this piece of code from: Router
> Handler
> Any Other layer
> WebClient
@TestPropertySource(properties = {
"spring.config.location=classpath:application-test.yml",
"spring.profiles.active=your-profile"
})
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class MyClassTest {
@Autowired
protected WebTestClient webTestClient;
@MockBean
private WebClient myCustomWebClient;
@MockBean
private WebClient.ResponseSpec responseSpec;
@MockBean
@SuppressWarnings("rawtypes")
private WebClient.RequestHeadersUriSpec requestHeadersUriSpec;
@SuppressWarnings("rawtypes")
private WebClient.RequestHeadersSpec requestHeadersSpec;
@Test
@SuppressWarnings("unchecked")
void testCustomMethod() {
Mockito.when(requestHeadersSpec.headers(Mockito.any()))
.thenReturn(requestHeadersSpec);
Mockito.when(requestHeadersSpec.retrieve())
.thenReturn(responseSpec);
Mockito.when(myCustomWebClient.get())
.thenReturn(requestHeadersUriSpec);
Mockito.when(requestHeadersUriSpec.uri("/my/downstream"))
.thenReturn(requestHeadersSpec);
Mockito.when(responseSpec.bodyToMono(String.class))
.thenReturn(Mono.empty());
Mockito.when(myCustomWebClient.get().uri("/my/downstream")
.headers(Mockito.any()).retrieve().bodyToMono(String.class))
.thenReturn(Mono.empty());
webTestClient.post()
.uri("/your-end-point")
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.header("header1", "header1Value")
.bodyValue(YourRequestClass.builder().build())
.exchange()
.expectStatus()
.is5xxServerError()
.expectBody(YourErrorClassPojo.class)
.value(yourErrorResponsePojo -> {
// Assertions.assertEquals();
})
.consumeWith(System.out::println);
}
Upvotes: 0