Writing JUnit tests for restTemplate.delete with Observable throwing OnErrorNotImplementedException - spring-boot

I have two methods in my Spring Boot(version 1.3.3) project as follows;
public Observable<Void> methodA() {
return this.methodB().map(s -> {
methodC.subscribe( aVoid ->
// some code
);
// some code
});
}
private Observable<Void> methodC() {
return Observable<~>create(sub -> {
restTemplate.delete(//some code);
sub.onNext(null);
sub.onCompleted();
}).doOnNext(//some code)
.doOnError(//some code);
}
I am using rxjava version 1.3.8.
I am trying to write Junit tests for methodC where restTemplate.delete(//some code); throws a HttpClientErrorException. For that, mocked the restTemplate.delete(//some code); as follows;
#Test
public void test() {
// some code
Mockito.doThrow(new HttpClientErrorException(HttpStatus.NOT_FOUND)).when(restTemplate)
.delete(//some code);
// some code
final Observable<Void> result = methodA();
final TestSubscriber<Void> subscriber = new TestSubscriber();
result.subscribe(subscriber);
subscriber.assertNotCompleted();
subscriber.assertError(HttpClientErrorException.class);
}
The mock is working as expected but it throws rx.exceptions.OnErrorNotImplementedException: 404 NOT_FOUND and fails the test.
but when I run the code, it is working as expected.
What am I missing here? How can I mock restTemplate.delete(// some code); to throw HttpClientErrorException and assert it?
Thanks in advance!

Related

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

How do you assert with delete method void

I'm unit testing a Spring boot web app with Mockito. One of my methods is returning a void, but if I try to test it, I get compilation errors.
This is the test I wrote:
public void testDeleteActor()throws NetflixException {
when(actorRepository.findById(1L)).thenReturn(Optional.of(Mockito.any(Actor.class)));
assertEquals(null, service.deleteActorById(Mockito.anyLong());
}
And this is the method I'm trying to test:
#Override
public void deleteActorById(Long id) throws NetflixException {
Actor actor = actorRepository
.findById(id)
.orElseThrow(() -> new NotFoundException("Actor id not found - " + id));
actorRepository.delete(actor);
}
As you can see in the following screenshot, I'm getting an error with my assertEquals() statement:
In your code, your method isn't actually returning null, it's returning nothing (void). That means that you can't write assertions based on what the method returns. This is the reason why the assertEquals() statement is giving you an error.
In stead of testing what the method returns, you can test the expected behaviour of the method. In this example, there are three things we expect:
The method should retrieve the actor by its ID.
The method should throw an exception if no actor was found with the given ID.
The method should delete the actor if it was found.
To implement this these tests, you can use Mockito's verify() and AssertJ's assertThatExceptionOfType(). For example:
#Test
void deleteActorById_retrievesActorByID() {
Actor actor = new Actor();
when(actorRepository.findById(1L)).thenReturn(Optional.of(actor));
service.deleteActorById(1L);
verify(repository).findById(1L);
}
#Test
void deleteActorById_throwsExceptionIfIDNotFound() {
assertThatExceptionOfType(NotFoundException.class)
.isThrownBy(() -> service.deleteActorById(1L))
.withMessage("Actor id not found - 1");
}
#Test
void deleteActorById_deletesActorIfIDFound() {
Actor actor = new Actor();
when(actorRepository.findById(1L)).thenReturn(Optional.of(actor));
service.deleteActorById(1L);
verify(actorRepository).delete(actor);
}

thenThrow() not throwing an exception

I have a method in OneServiceImpl class as follows. In that class I am calling an interface method from another class.
public class OneServiceImpl {
//created dependency
final private SecondService secondService;
public void sendMessage(){
secondService.validateAndSend(5)
}
}
public interface SecondService() {
public Status validateAndSend(int length);
}
public class SecondServiceImpl {
#Override
public Status ValidateAndSend(int length) {
if(length < 5) {
  throw new BadRequestException("error", "error");
}
}
}
Now when I am try to perform unit test on OneServiceImpl I am not able to throw a BadRequestException.
when(secondService.validateAndSend(6)).thenThrow(BadRequestException.class);
Not quite sure what your use case is, but I think you should write an own test to accept and test an exception.
#Test(expected = BadRequestException.class)
public void testValidateAndSend(){
SecondService secondService = new SecondService();
secondservice.ValidateAndSend(6); //method should be lowercase
}
Not sure this is the case considering you didn't post a full example of code + unit tests, but your mock will throw only when you are passing 6 as parameter. When configuring the behaviour of your mock with when you are telling it to throw only when the validateAndSend method is called with parameter 6.
when(secondService.validateAndSend(6)).thenThrow(...)
In your code you have 5 hardcoded. So that mock will never throw for the code you have, because it's configured to react to an invocation with parameter 6 but the actual code is always invoking it passing 5.
public void sendMessage(){
secondService.validateAndSend(5)
}
If the value passed to the mock is not important you could do something like the following, that will throw no matter what's passed to it:
when(secondService.validateAndSend(any())).thenThrow(BadRequestException.class);
On the other hand, if the value is important and it has to be 5 you could change the configuration of your mock with:
when(secondService.validateAndSend(5)).thenThrow(BadRequestException.class)

How to mock Spring Webclient

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());
}

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