Причина, по которой вызывается метод service2.doSomething()
, заключается в том, что, хотя Mono
может быть ленивым, просто вызов оператора - нет. Вы с нетерпением вызываете методы, которые будут возвращать ленивые Mono
s, таким образом собирая конвейер обработки.
Если вы вставите свой код в код, он станет немного понятнее:
//exception is CREATED immediately, but USED lazily
return Mono.error(new IllegalStateException())
//mono is CREATED immediately. The data it will emit is also CREATED immediately. But it all triggers LAZILY.
.then(Mono.just(new SomeResponse()))
//note that then* operators completely ignore previous step's result (unless it is an error)
.thenReturn(new SuccessResponse("Awesome")));
Некоторые операторы принимают Supplier
или Function
, что обеспечивает ленивую альтернативу этому нетерпеливому стилю конструирования. Один универсальный способ сделать это - использовать Mono.defer
:
public Mono<SuccessResponse> doSomething(){
return service1.doSomething()
.then(Mono.defer(service2::doSomething2))
.thenReturn(new SuccessResponse("Awesome")));
}
Но я бы сказал, что , , если только service2
не скрывает источник, который НЕ является ленивым (например, Mono
, адаптированный из CompletableFuture
) , проблема не в doSomething
, а в тесте .
С помощью макета service2
вы по существу тестируете сборку цепочки операторов, но не в том случае, если этот шаг в конвейере действительно выполняется.
Один из трюков, доступных в reactor-test
, - это обернуть Mono.just
/ Mono.error
в PublisherProbe
. Это может быть использовано для насмешки над Mono
, как вы это сделали, но с добавленной функцией предоставления утверждений о выполнении Mono
: была ли она подписана? это было запрошено?
//this is ultimately tested by the assertThrownBy, let's keep it that way:
when(service1.doSomething()).thenReturn(Mono.error(new IllegalStateException("Something bad happened")));
//this will be used to ensure the `service2` Mono is never actually used:
PublisherProbe<SomeResponse> service2Probe = PublisherProbe.of(Mono.just(new SomeResponse()));
//we still need the mock to return a Mono version of our probe
when(service2.doSomething()).thenReturn(service2Probe.mono());
assertThatThrownBy(() -> testedService.doSomething().block())
.isExactlyInstanceOf(IllegalStateException.class);
//service2 might have returned a lazy Mono, but it was never actually used:
probe.assertWasNotSubscribed();