I am using Java Reactor in a Spring-boot project, where I need to cache my data in one of the intermediate steps. It's on Spring Boot 3, Java 17, Junit5...
My caching service looks like this:
@Component
public class CachingService {
private static final int DURATION = 60;
public Mono<Resource> ofMono(final UUID guid, final Mono<Resource> mono) {
final String cachingKey = "key";
final Function<String, Mono<Resource>> monoFn = ofMono(key -> mono);
return Mono.defer(() -> monoFn.apply(cachingKey));
}
private Function<String, Mono<Resource>> ofMono(final Function<String, Mono<Resource>> fn) {
final AsyncLoadingCache<String, Resource> cache =
Caffeine.newBuilder()
.expireAfterWrite(Duration.ofSeconds(DURATION).multipliedBy(2))
.refreshAfterWrite(Duration.ofSeconds(DURATION))
.buildAsync((k, e) -> fn.apply(k).subscribeOn(Schedulers.fromExecutor(e)).toFuture());
return (k) -> Mono.fromFuture(cache.get(k));
}
}
So my purpose is caching an instance of 'Resource' record, which is published as Mono from another service, and will be consumed in another service again.
I am injecting cachingService in another service and calling it's method:
@Override
public Mono<Resource> getResourceWithAccess(final UUID guid) {
final Mono<Boolean> viewAccess = accessService.getViewAccess(guid);
return viewAccess
.flatMap(hasAccess -> mapToResource(guid, hasAccess))
.flatMap(resource -> cachingService.ofMono(guid, Mono.just(resource)));
}
The accessService.getViewAccess and mapToResource used in the code:
Mono<Boolean> accessService.getViewAccess(UUID fileGuid);
Mono<Resource> mapToResource(
final UUID guid, final Boolean hasAccess);
I have a Mockito test like this:
@Test
void test() {
// GIVEN
...
private ResourceWithAccessService service
@Mock private CachingService cachingService;
Resource expectedResource = ...
when(cachingService.ofMono(fileGuid, Mono.just(expectedResource ))).thenReturn(Mono.just(expectedResource ));
// WHEN
final Mono<Resource > actualResource =
service.getResourceWithAccess(fileGuid);
// THEN
StepVerifier.create(actualResource).expectNext(expectedResource).verifyComplete();
}
Unfortunately, I am getting this error:
java.lang.AssertionError: expectation ....
actual: onError(org.mockito.exceptions.misusing.PotentialStubbingProblem:
Strict stubbing argument mismatch. Please check:
- this invocation of 'ofMono' method:
cachingService.ofMono(
98f0b409-20b9-3674-a0e6-eb5048219f2c,
MonoJust
);
-> at com...lambda$getResourceWithAccess$1(ResourceWithAccessServiceImp.java:52)
- has following stubbing(s) with different arguments:
1. cachingService.ofMono(
98f0b409-20b9-3674-a0e6-eb5048219f2c,
MonoJust
);
I have found a workaround finally. By using ArgumentCaptor, I am still able to check if my method is called with correct parameters.
private void verifyCachingService(final UUID fileGuid, final Resource resource) {
final ArgumentCaptor<Mono> acMono = ArgumentCaptor.forClass(Mono.class);
verify(cachingService).ofMono(eq(fileGuid), acMono.capture());
StepVerifier.create(acMono.getValue()).expectNext(resource).verifyComplete();
}