How to mock Spring Webclient - spring-boot

I am trying to write a unit test case using mockito but getting null pointer exception when calling retrieve. Can someone help me how to fix this?
Method
public Mono<String> retrieveSites() {
return restAPIClient.get().uri(builder -> builder.path("/someurl")
.queryParam("param1","value1")
.queryParam(param2,"value2")
.build())
**.retrieve()**
.onStatus(HttpStatus::is4xxClientError, this::processClientError)
.onStatus((HttpStatus::isError), this::processServerError).bodyToMono(String.class);
}
TestCase
#Test
public void shouldReturnRetrieveSites() {
when(webClient.get()).thenReturn(requestHeadersUriSpec);
when(builder.path(anyString())).thenReturn(builder);
when(builder.queryParam(anyString(), anyString())).thenReturn(builder);
when(builder.path(anyString())).thenReturn(builder);
when(requestHeadersSpec.retrieve()).thenReturn(responseSpec);
when(responseSpec.bodyToMono(ArgumentMatchers.<Class<String>>notNull())).thenReturn(Mono.empty());
assertNotNull(webClientService.retrieveSites());
}

Related

UNIT TEST: queryForObject method not fully covered the lines

I am trying to mock queryForObject method using Mockito. Unit test actually is passed but the lines is not fully covered.
The code to get the people object is like below:
jdbcTemplate.queryForObject(GET_PEOPLE,
(rs, rowNum) -> new People()
.setId(rs.getInt("id"))
.setFirstName(rs.getString("first_name"))
.setLastName(rs.getString("last_name")),
department, position);
FYI: GET_PEOPLE is a static constant contain the SQL query.
and the unit test is:
People people = new People();
people.setId(1);
people.setFirstName("John");
people.setLastName("Doe");
when(jdbcTemplate.queryForObject(any(), (RowMapper<Object>) any(), any())).thenReturn(people);
Can anyone let me know how to mock to get the lines fully covered. Thanks in advance.
You are not getting coverage because you never execute that code.
You need to call your rowmapper:
#ExtendWith(MockitoExtension.class)
public class MyTest {
#Mock
JdbcTemplate jdbcTemplate;
#Mock
ResultSet resultSet;
#Test
public void myTest() throws SQLException {
when(resultSet.getInt(1)).thenReturn(1);
...
when(jdbcTemplate.queryForObject(any(), (RowMapper<People>) any(), any())).thenAnswer(new Answer<People>() {
#Override
public People answer(InvocationOnMock invocationOnMock) throws Throwable {
RowMapper<People> rowMapper = invocationOnMock.getArgument(1);
return rowMapper.mapRow(resultSet, 1);
}
});
...
}
}

Unit testing GatewayFilter causes NullPointerException

I'm trying to unit test my GatewayFilter, however I'm having troubles running even simple test.
This is small example of what is failing right now
#ExtendWith(MockitoExtension.class)
public class SomeFilterTest {
private final GatewayFilter gatewayFilter = (exchange, chain) ->
Mono.just("Hello")
.flatMap(this::doSomething)
.switchIfEmpty(Mono.defer(() -> chain.filter(exchange)));
private Mono<Void> doSomething(String value) {
System.out.println(value);
return Mono.empty();
}
#Test
void test1() {
var exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/").build());
var chain = mock(GatewayFilterChain.class);
gatewayFilter.filter(exchange, chain).block();
}
}
Unfortunatelly, it is failing because of
The Mono returned by the supplier is null
java.lang.NullPointerException: The Mono returned by the supplier is
null at java.base/java.util.Objects.requireNonNull(Objects.java:246)
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:44) at
reactor.core.publisher.Mono.subscribe(Mono.java:4361)
And to be honest, I have no idea why is that happening?
You have not stubbed out the filter method call on your mock object, GatewayFilterChain. As a result, the supplier () -> chain.filter(exchange) returns null. You are not allowed to create a Mono with a value of null, hence the exception.
As a result your test should look something like
#Test
public void test1() {
var exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/").build());
var chain = mock(WebFilterChain.class);
// stubbing behaviour on our mock object
given(chain.filter(exchange)).willReturn(Mono.empty());
gatewayFilter.filter(exchange, chain).block();
}
Additionally, I would suggest using StepVerifier instead of using block() in unit tests. This is provided by reactor-test and is purpose built for unit testing reactive code
#Test
public void test1() {
var exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/").build());
var chain = mock(WebFilterChain.class);
given(chain.filter(exchange)).willReturn(Mono.empty());
StepVerifier.create(gatewayFilter.filter(exchange, chain))
.verifyComplete();
}
Here is a very useful Step Verifier Tutorial to help you get started

Errors: UnfinishedStubbing

I am writing Junit test case and I want to mock KafkaTemplate method kafkaTemplate.send(TOPIC_NAME, "someData");. In my project, I am using spring boot and Kafka.
Below is the StudentRecords class. I am using mockito for mocking the dependencies.
#Component
public class StudentRecords {
#Autowired
private KafkaTemplate<String, String> kafkaTemplate;
#Value("${topicNameForStudents}")
private String TOPIC_NAME;
public String sendStudentData(StudentDTO studentDTO) {
String studentStr = null;
try {
if(null == studentDTO) {
throw new StudentException("studentDTO Object cant be null");
}
if(studentDTO.getId() == null) {
throw new StudentException("Id cant be empty");
}
ObjectMapper mapper = new ObjectMapper();
studentStr = mapper.writeValueAsString(srvgExecution);
kafkaTemplate.send(TOPIC_NAME, studentStr);
return "SUCCESS";
} catch (JsonProcessingException e) {
e.printStackTrace();
return "ERROR";
}
}
}
And test class is as follows:
#ExtendWith({ SpringExtension.class, MockitoExtension.class })
class StudentRecordsTest {
#InjectMocks
StudentRecords studentRec;
#Mock
private KafkaTemplate<String, String> kafkaTemplate;
#Test
void testSendStudentData() {
StudentDTO studentDTO = new StudentDTO();
studentDTO.setId(1);
studentDTO.setName("ABC");
studentDTO.setAddress("Some Address");
Mockito.when(kafkaTemplate.send(Mockito.anyString(), Mockito.anyString()));
studentRec.sendStudentData(studentDTO);
}
}
And I getting the following error
[ERROR] Errors:
[ERROR] studentRec.testSendStudentData: ยป UnfinishedStubbing
It is happening at line studentRec.sendStudentData(studentDTO);
How I can resolve/write the junit for this?
#Test
void testSendStudentData() {
StudentDTO studentDTO = new StudentDTO();
studentDTO.setId(1);
studentDTO.setName("ABC");
studentDTO.setAddress("Some Address");
Mockito.when(kafkaTemplate.send(Mockito.anyString(), Mockito.anyString()));
studentRec.sendStudentData(studentDTO);
Mockito.verify(kafkaTemplate).send(Mockito.anyString(), Mockito.anyString());
}
after updating the junit to above one, ended up with below error at this statement Mockito.verify(kafkaTemplate).send(Mockito.anyString(), Mockito.anyString());
Argument(s) are different! Wanted:
kafkaTemplate.send(
<any string>,
<any string>
);
Your mock statement is incomplete.
Mockito.when(kafkaTemplate.send(Mockito.anyString(), Mockito.anyString()));
KafkaTemplate's send method returns a ListenableFuture object and hence you need to have your mock return it.
I understand, you are not really using the returned value as part of your code.
In that case you may simply return null as below.
Mockito.when(kafkaTemplate.send(Mockito.anyString(), Mockito.anyString())).thenReturn(null);
Although, I would suggest you should ideally check for return value for better error handling, but that can be a different story altogether.
In case you do plan to handle error by checking the return value, your above mock statement can be written to return both success and fail cases.
You may check below thread for more details on how to set the correct mock expectations for KafkaTemplate.
How to mock result from KafkaTemplate

class java.util.LinkedList cannot be cast to class org.reactivestreams.Publisher

I am try to create a unit test using Junit and Mockito. However I am getting an error saying
class java.util.LinkedList cannot be cast to class org.reactivestreams.Publisher
When the it clearly returns a list of strings.
test
#Test
public void fullLoad() {
when((Publisher<?>) this.mockedProductComponent.getErpNumbers("US", "es")).thenReturn(
just(new ArrayList<>())
);
}
method being tested
public boolean fullLoad(String country, String language) {
List<String> erpNumbers = productComponent.getErpNumbers(country, language);
log.info("Retrieved following ERP numbers: {}", erpNumbers);
Lists.partition(erpNumbers, batchSize)
.forEach(handleBatch(country, language));
return true;
}
method trying to mock
public List<String> getOnlineErpNumbers(String country, String lang) {
return webClient.get()
.uri(uriBuilder -> uriBuilder
.scheme(scheme)
.host(host)
.path(path)
.port(port)
.pathSegment(country)
.pathSegment(lang)
.pathSegment("erp")
.build())
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(new ParameterizedTypeReference<List<String>>(){})
.timeout(Duration.ofMillis(3000))
.onErrorReturn(Collections.emptyList())
.block();
}
I know it must be because I am doing this the reactive way but I have not been able to find documentation on how to test such a process.
You confused your imports.
You are using Mono.when from Reactor which accepts a publisher:
public static Mono<Void> when(Publisher<?>... sources)
instead of Mockito.when
public static <T> OngoingStubbing<T> when(T methodCall)

Mockito mock the constructor based api call invocation

I have the following service which has the executeSofortRequest which makes a call to the third party api
#RequiredArgsConstructor
public class SofortRequestService {
public com.sofort.lib.payment.products.response.PaymentResponse executeSofortRequest(com.sofort.lib.payment.products.request.PaymentRequest sofortRequest,
ExternalPaymentInfoEntity externalPaymentInfo) {
com.sofort.lib.payment.products.response.PaymentResponse sofortResponse;
try {
sofortResponse = new DefaultSofortLibPayment(customerId, apiKey).sendPaymentRequest(sofortRequest);
} catch (HttpAuthorizationException e) {
saveExternalPaymentInfo(externalPaymentInfo, e);
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, SOFORT_AUTHORIZATION_FAILED_WITH_GIVEN_APIKEY, e);
} catch (ConnectionException e) {
saveExternalPaymentInfo(externalPaymentInfo, e);
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, SOFORT_COMMUNICATION_FAILED, e);
}
return sofortResponse;
}
}
Now I have mocked this in my test
#RunWith(MockitoJUnitRunner.class)
public class SofortRequestServiceTest {
#Mock
private ExternalPaymentInfoRepository externalPaymentInfoRepository;
#InjectMocks
private SofortRequestService sofortRequestService;
#Test
public void test_executeSofortRequest() throws JsonProcessingException {
given(new DefaultSofortLibPayment(1234, "test-api-key").sendPaymentRequest(sofortPaymentRequest)).willThrow(HttpAuthorizationException.class);
//When
assertThatThrownBy(() -> sofortRequestService.executeSofortRequest(sofortPaymentRequest, externalPaymentInfoEntity))
.isInstanceOf(HttpAuthorizationException.class);
//Then
verify(externalPaymentInfoRepository, times(1))
.save(ExternalPaymentInfoEntity.builder()
.referenceTransaction(paymentRequest.getTransactionId())
.customerId(paymentRequest.getPaymentDocument()
.getCustomer()
.getCustomerId())
.eventType(OUTGOING)
.paymentType("sofort checkout")
.action(AUTH)
.requestData(new ObjectMapper().writeValueAsString(paymentRequest))
.success(false)
.build());
}
}
My problem is that when the test execute and runs line
given(new DefaultSofortLibPayment(1234, "test-api-key").sendPaymentRequest(sofortPaymentRequest)).willThrow(HttpAuthorizationException.class);
it is running the actual implementation and not the mock and then it fails to exuecute further.
How can I write an integration test for executeSofortRequest method
This may not be the answer you want,
but in your situation I would add a package access zero (0) parameter constructor
as a way to support mocking during unit tests.
In this situation,
make sure that the unit test is in the same package as the class it is testing.

Resources