I am writing unit testing for function calculate in webflux
@Override
public Mono<SearchSessionAndETAResponse> calculate(DTO sessionRequest) throws ExecutionException, InterruptedException {
List<GeospatialData> dataFromRedis = getDataFromRedis(sessionRequest.getSessionId());
return Mono.just(dataFromRedis)
.map(data -> {
System.out.println("data::"+data);
return "entityIdentifiers";
})
.flatMap(entityIdentifiers -> {
System.out.println("entityIdentifiers::"+entityIdentifiers);
return etaService.entitiesEta("entityIdentifiers");
})
.map(wayPointRoute -> {
return searchSessionAndETAResponse;
});
}
below is the test case
@ExtendWith(MockitoExtension.class)
public class BroadCastingServiceImplTest {
@InjectMocks
private BroadCastingServiceImpl broadCastingService;
@Mock
private EtaService etaService;
@BeforeEach
void setUp() {
closeable = MockitoAnnotations.openMocks(this);
}
@AfterEach
void tearDown() throws Exception {
closeable.close();
}
@Test
@DisplayName("Given a valid request, when SearchSession and calculate eta is called, it should search Broadcasting session and return eta")
void givenValidRequest_whenSearchSessionAndCalculateEta_shouldReturnBroadCastingResponseDTO() throws ExecutionException, InterruptedException, IOException {
// GIVEN
UUID sessionId = UUID.randomUUID();
DTO searchSessionAndCalculateETA = new DTO();
searchSessionAndCalculateETA.setSessionId(sessionId.toString());
searchSessionAndCalculateETA.setWaypoints(List.of("waypointsDTO", "waypointsDTO2"));
given(etaService.entitiesEta("entitiesEtaRequestDTO")).willReturn(Mono.just(List.of("entitiesEtaResponseDTO")));
// WHEN
Mono<SearchSessionAndETAResponse> result = broadCastingService.searchSessionAndCalculateETA(searchSessionAndCalculateETA);
}
}
The problem:
when i run test case the call does not go inside .map, .flatMap and .map even though dataFromRedis is not null. Is there anything i am missing ?
In your test you just create the Mono
but you don't actually "run" it, more precisely you do not subscribe to it.
Working with reactive code has two phases:
First you write the specification of the reactive stream, which tells what to do, but doesn't start the process. A Mono
is only a fancy complex memory structure with a lot of lambda functions telling what will be done when it will be done.
Then you subscribe to the Mono
- only this starts the whole process and starts calling all the lambdas. Only then the "call is going inside .map and .flatMap" to paraphrase your words :)
Read some tutorial, e.g. this Baeldung's Intro To Reactor Core.
If you just use Mono
in simple Spring applications to handle REST endpoint, you may easily miss this because your @RestController
just returns the Mono
and the rest (pun intended) is handled by Spring. Spring subscribes to the Mono
you returned from the controller methods and handles that the result is passed to the called via HTTP.
In unit tests, you can do it very simply just by calling its block()
method, or more properly by using StepVerifier
:
result.as(StepVerifier::create)
.expectNext(objectToExpect)
.verifyComplete();
Just using the block()
method works more like a workaround. StepVerifier
is the proper way of unit testing the reactive code.
Be careful not to just use the subscribe()
method, because the test may finish before the subscription has time to perform and you will get unpredictable results.