Spring WebFlux: Unit testing of Mono - unit-testing

I have the following service
#Service
public class TestService {
#Autowired
TestService1 testService1;
#Autowired
TestService2 testService2;
#Autowired
TestService3 testService3;
#Autowired
TestService4 testService4;
public Mono<DataResponse> getData() {
Mono<String> ts1 = testService1.getMono();
Mono<String> ts2 = testService2.getMono();
Mono<String> ts3 = testService3.getMono();
Mono<String> ts4 = testService4.getMono();
return Mono.zip(ts1, ts2, ts3, ts4)
.flatmap(resp -> {
// line XX
return(Mono.just(new DataResponse(ts1, ts2, ts3, ts4)));
});
}
}
I'm trying to perform unit testing using Mockito and JUnit.
#SpringBootTest()
public class TestService {
#InjectMocks
TestService testService;
#Mock(answer = Answers.RETURNS_DEEP_STUBS)
TestService1 testService1;
#Mock(answer = Answers.RETURNS_DEEP_STUBS)
TestService2 testService2;
#Mock(answer = Answers.RETURNS_DEEP_STUBS)
TestService3 testService3;
#Mock(answer = Answers.RETURNS_DEEP_STUBS)
TestService4 testService4;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
#Test
public Mono<DataResponse> getData() {
when(testService1.getMono()).thenReturn(Mono.just(new String()));
when(testService2.getMono()).thenReturn(Mono.just(new String()));
when(testService3.getMono()).thenReturn(Mono.just(new String()));
when(testService4.getMono()).thenReturn(Mono.just(new String()));
Mono<DataResponse> res = testService.getData();
Predicate<DataResponse> p = response -> response != null;
StepVerifier.create(res.log(), 1).expectNextMatches(p).verifyComplete();
}
}
StepVerifier never executes expectNextMatches if I return Mono.zip(...) from the getData method and never executes code inside flatmap (line XX), the publisher is getting initialized but not able to subscribe to it. But if I return Mono.just() it is working fine.
Following are the logs
12:47:18.576 [main] INFO reactor.Mono.FlatMap.2 - | onSubscribe([Fuseable] MonoFlatMap.FlatMapMain)
12:47:18.587 [main] INFO reactor.Mono.FlatMap.2 - | request(1)
What would be the best approach to write unit test cases for this use case with 100% coverage?

Related

Mocked repository is null when Mockito testing

I end up with my repository as null when testing a method in a service class. It is unclear what causes this for me.
In my service class I make use of a private RestTemplate that I setup as follows together with repository as #Mocks and service class #Injectmocks on :
...
#InjectMocks
#Spy
WorkService workService;
#Mock
private WorkJobRepository workJobRepository;
...
#Mock
RestTemplateBuilder mockedBuilder= Mockito.mock(RestTemplateBuilder.class);
#Mock
RestTemplate mockedRestTemplate = Mockito.mock(RestTemplate.class);
#BeforeEach
void setUp() {
ReflectionTestUtils.setField(listToPrintService, "restTemplate", mockedRestTemplate);
}
My test case in which repository is not passed (it is null):
#Test
void testGetRequestListToWork() {
ListToWork listToWork = getListToWork();
WorkJob WorkJob = getOneWorkJob();
ResponseEntity<String> responseEntity = getOneResponseEntityString(listToWork);
Mockito.doReturn(responseEntity).when(listToWorkService).getRequestListToWork(listToWork);
Mockito.when(listToWorkService.getRequestListToWork(listToWork)).thenAnswer(new Answer<ResponseEntity<String>>() {
#Override
public ResponseEntity<String> answer(InvocationOnMock invocation) {
return responseEntity;
}
});
String json = responseEntity.getBody().toString();
Mockito.when(listToWorkService.createNewWorkJob(json, listToWork)).thenReturn(WorkJob);
listToWorkService.getRequestListToWork(WorkJob);
assertEquals("xxx", repository.getById(listToWork.getId));
}
Here's the method I am trying to test:
public WorkJob getRequestListToWork(WorkJob WorkJob) {
ListToWork listToWork = new ListToWork(WorkJob.getTemplateId(), WorkJob.getStoreName(), WorkJob.getJobListId(), WorkJob.getId());
ResponseEntity<String> responseEntity = getRequestListToWork(listToWork);
Gson g = new Gson();
String json = responseEntity.getBody().toString();
WorkJob newWorkJob = createNewWorkJob(json,listToWork);
if(newWorkJob.getJobId() != -1 && newWorkJob.getJobState() != 12) {
pollForJobStatus(newWorkJob);
}
return newWorkJob;
}
Can anybody advise? What is causing the repository not to be passed?

Spring-Webflux: Handler function unit test is throwing UnsupportedMediaTypeStatusException

I am trying to write Unit test to the handler function, I followed the example from the Spring project. Can someone help me why the following test is throwing UnsupportedMediaTypeStatusException?
Thanks
Handler function
public Mono<ServerResponse> handle(ServerRequest serverRequest) {
log.info("{} Processing create request", serverRequest.exchange().getLogPrefix());
return ok().body(serverRequest.bodyToMono(Person.class).map(p -> p.toBuilder().id(UUID.randomUUID().toString()).build()), Person.class);
}
Test Class
#SpringBootTest
#RunWith(SpringRunner.class)
public class MyHandlerTest {
#Autowired
private MyHandler myHandler;
private ServerResponse.Context context;
#Before
public void createContext() {
HandlerStrategies strategies = HandlerStrategies.withDefaults();
context = new ServerResponse.Context() {
#Override
public List<HttpMessageWriter<?>> messageWriters() {
return strategies.messageWriters();
}
#Override
public List<ViewResolver> viewResolvers() {
return strategies.viewResolvers();
}
};
}
#Test
public void handle() {
Gson gson = new Gson();
MockServerWebExchange exchange = MockServerWebExchange.from(
MockServerHttpRequest.post("/api/create")
.body(gson.toJson(Person.builder().firstName("Jon").lastName("Doe").build())));
MockServerHttpResponse mockResponse = exchange.getResponse();
ServerRequest serverRequest = ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders());
Mono<ServerResponse> serverResponseMono = myHandler.handle(serverRequest);
Mono<Void> voidMono = serverResponseMono.flatMap(response -> {
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
boolean condition = response instanceof EntityResponse;
assertThat(condition).isTrue();
return response.writeTo(exchange, context);
});
StepVerifier.create(voidMono)
.expectComplete().verify();
StepVerifier.create(mockResponse.getBody())
.consumeNextWith(a -> System.out.println(a))
.expectComplete().verify();
assertThat(mockResponse.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_JSON);
}
}
Error Message:
java.lang.AssertionError: expectation "expectComplete" failed (expected: onComplete(); actual: onError(org.springframework.web.server.UnsupportedMediaTypeStatusException: 415 UNSUPPORTED_MEDIA_TYPE "Content type 'application/octet-stream' not supported for bodyType=com.example.demo.Person"))
I found that I missed .contentType(MediaType.APPLICATION_JSON) to my mock request.
MockServerWebExchange.from(
MockServerHttpRequest.post("/api/create").contentType(MediaType.APPLICATION_JSON)
.body(gson.toJson(Person.builder().firstName("Jon").lastName("Doe").build())));
fixed my issue.

Unit Test Async Deferred Result Controller gets hung forever

The controller method I am testing
#GetMapping("/customers")
#ResponseBody
public DeferredResult<ResponseEntity<Resources<Resource<Customer>>>> getAllCustomers(
#PageableDefault(page = 0, size = 20) #SortDefault.SortDefaults({
#SortDefault(sort = "name", direction = Direction.ASC) }) Pageable pageable,
PagedResourcesAssembler<Customer> assembler, HttpServletRequest request) {
DeferredResult<ResponseEntity<Resources<Resource<Customer>>>> response = new DeferredResult<>(
Long.valueOf(1000000));
response.onTimeout(() -> response
.setErrorResult(ResponseEntity.status(HttpStatus.REQUEST_TIMEOUT).body("Request timed out.")));
response.onError((Throwable t) -> {
response.setErrorResult(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("An error occured."));
});
ListenableFuture<Page<Customer>> future = customerService.findAll(pageable);
future.addCallback(new ListenableFutureCallback<Page<Customer>>() {
#Override
public void onSuccess(Page<Customer> result) {
Link self = new Link(
ServletUriComponentsBuilder.fromRequestUri(request).buildAndExpand().toUri().toString(),
"self");
LOGGER.debug("Generated Self Link {} for Customer Resource Collection", self.getHref());
if (result.hasContent())
response.setResult(
ResponseEntity.ok(assembler.toResource(result, customerResourceAssembler, self)));
else
response.setErrorResult(ResponseEntity.notFound());
LOGGER.debug("Returning Response with {} customers", result.getNumber());
}
#Override
public void onFailure(Throwable ex) {
LOGGER.error("Could not retrieve customers due to error", ex);
response.setErrorResult(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Could not save customers list due to server error."));
}
});
return response;
}
the unit test
#RunWith(SpringRunner.class)
#WebMvcTest(CustomerController.class)
#EnableSpringDataWebSupport
#Import({ CustomerResourceAssember.class, BranchResourceAssembler.class, InvoiceResourceAssembler.class,
CustomerAsyncService.class })
public class CustomerControllerTests {
#Autowired
private MockMvc mockMvc;
#Autowired
CustomerAsyncService customerService;
#MockBean
private CustomerRepository customerRepository;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testWhenNoCustomersThenReturnsEmptyHALDocument() throws Exception {
// Given
BDDMockito.given(customerRepository.findAll(PageRequest.of(0, 20)))
.willReturn(new PageImpl<Customer>(Collections.emptyList()));
// When
MvcResult result = mockMvc.perform(get("/customers").accept(MediaTypes.HAL_JSON_VALUE)).andDo(print())
.andExpect(request().asyncStarted())
.andExpect(request().asyncResult(new PageImpl<Customer>(Collections.emptyList()))).andReturn();
// Then
mockMvc.perform(asyncDispatch(result)).andExpect(status().isOk());
}
This test neve completes, doesn't even time out on my IDE, I have to kill it everytime I run it, if run the entire app however this /customers endpoint gives a 404 when there are no customers added to the application.
What do I need to do make sure this test completes, the CustomerService call ultimately calls CustomerRepository which I have mocked because I couldn't get my brains around how to mock the async call to service method. the customer service class is as follows
#Async
#Service
public class CustomerAsyncService {
private CustomerRepository customerRepository;
#Autowired
public CustomerAsyncService(CustomerRepository customerRepository) {
this.customerRepository = customerRepository;
}
#Transactional(readOnly = true, isolation = Isolation.SERIALIZABLE)
public ListenableFuture<Page<Customer>> findAll(Pageable pageable) {
return AsyncResult.forValue(customerRepository.findAll(pageable));
}
I was hoping mocking the Repository method would do the trick. How do I mock the async service call
My bad was using mocks wrongly, this worked
#RunWith(SpringRunner.class)
#WebMvcTest(CustomerController.class)
#Import({ CustomerResourceAssember.class, BranchResourceAssembler.class, InvoiceResourceAssembler.class,
CustomerAsyncService.class })
public class CustomerControllerTests {
#MockBean
private CustomerRepository customerRepository;
#InjectMocks
CustomerAsyncService customerService = new CustomerAsyncService(customerRepository);
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
JacksonTester.initFields(this, objectMapper);
}
#Test
public void testReturnsNotFoundForEmptyGetAllCustomersResult() throws Exception {
// Given
Page<Customer> emptyPage = new PageImpl<Customer>(Collections.emptyList());
BDDMockito.given(customerRepository.findAll(any(Pageable.class))).willReturn(emptyPage);
// When
MvcResult result = mockMvc.perform(get("/customers")).andExpect(request().asyncStarted()).andDo(print()).andReturn();
// Then
mockMvc.perform(asyncDispatch(result)).andDo(print()).andExpect(status().isNotFound());
}
}

Unit test verify method was called inside RxJava's doOnSuccess operator

I have the following code I'm trying to unit test :
if (networkUtils.isOnline()) {
return remoteDataSource.postComment(postId, commentText)
.doOnSuccess(postCommentResponse ->
localDataSource.postComment(postId, commentText))
.subscribeOn(schedulerProvider.io())
.observeOn(schedulerProvider.mainThread());
} else {
return Single.error(new IOException());
}
And this is how I'm trying to test it :
#Test
public void postComment_whenIsOnline_shouldCallLocalToPostComment() throws Exception {
// Given
when(networkUtils.isOnline())
.thenReturn(true);
String postId = "100";
String comment = "comment";
Response<PostCommentResponse> response = postCommentResponse();
when(remoteDataSource.postComment(anyString(), anyString()))
.thenReturn(Single.just(response));
// When
repository.postComment(postId, comment);
// Then
verify(localDataSource).postComment(postId, comment);
}
where I fake Response from Retrofit like :
private Response<PostCommentResponse> postCommentResponse() {
PostCommentResponse response = new PostCommentResponse();
response.setError("0");
response.setComment(postCommentResponseNestedItem);
return Response.success(response);
}
but it results to : Actually, there were zero interactions with this mock.
Any ideas ?
EDIT :
#RunWith(MockitoJUnitRunner.class)
public class CommentsRepositoryTest {
#Mock
private CommentsLocalDataSource localDataSource;
#Mock
private CommentsRemoteDataSource remoteDataSource;
#Mock
private NetworkUtils networkUtils;
#Mock
private PostCommentResponseNestedItem postCommentResponseNestedItem;
private CommentsRepository repository;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
BaseSchedulerProvider schedulerProvider = new ImmediateSchedulerProvider();
repository = new CommentsRepository(localDataSource, remoteDataSource, networkUtils, schedulerProvider);
}
// tests
}
When you want to test an Observable you have to subscribe to it so it will start emitting items.
As soon as I used :
TestObserver<Response<PostCommentResponse>> testObserver = new TestObserver<>();
and subscribed to :
repository.postComment(postId, comment)
.subscribe(testObserver);
the test worked as expected.

Retrofit Unit Test with Roboletric

Is there any possibility to test if Retrofit callback return success?
My code is quite simple:
#Config(constants = BuildConfig.class, sdk = 21,
manifest = "app/src/main/AndroidManifest.xml")
#RunWith(RobolectricGradleTestRunner.class)
public class RetrofitCallTest {
private MainActivity mainActivity;
#Mock
private RetrofitApi mockRetrofitApiImpl;
#Captor
private ArgumentCaptor<Callback<List<MyObject>>> callbackArgumentCaptor;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
ActivityController<MainActivity> controller = Robolectric.buildActivity(MainActivity.class);
mainActivity = controller.get();
RestClient.setApi(mockRetrofitApiImpl);
controller.create();
}
#Test
public void shouldFillAdapter() throws Exception {
Mockito.verify(mockRetrofitApiImpl)
.getYourObject(callbackAgrumentCaptor.capture());
int objectsQuantity = 10;
List<MyObject> list = new ArrayList<YourObject>;
for(int i = 0; i < objectsQuantity; ++i) {
list.add(new MyObject());
}
callbackArgumentCaptor.getValue().success(list, null);
ListAdapter adapter = mainActivity.getAdapter();
assertThat(adapter .getItemCount(), equalTo(objectsQuantity));
}
It's clear - I test if my code works correctly WHEN api return success.
But is there any posibility to test IF api return success?