Recently I noticed that my team follows two approaches on how to write tests in Reactor. First one is with help of .block()
method. And it looks something like that:
@Test
void set_entity_version() {
Entity entity = entityRepo.findById(ID)
.block();
assertNotNull(entity);
assertFalse(entity.isV2());
entityService.setV2(ID)
.block();
Entity entity = entityRepo.findById(ID)
.block();
assertNotNull(entity);
assertTrue(entity.isV2());
}
And the second one is about using of StepVerifier
. And it looks something like that:
@Test
void set_entity_version() {
StepVerifier.create(entityRepo.findById(ID))
.assertNext(entity -> {
assertNotNull(entity);
assertFalse(entity.isV2());
})
.verifyComplete();
StepVerifier.create(entityService.setV2(ID)
.then(entityRepo.findById(ID)))
.assertNext(entity -> {
assertNotNull(entity);
assertTrue(entity.isV2());
})
.verifyComplete();
}
In my humble opinion, the second approach looks more reactive I would say. Moreover, official docs are very clear on that:
A StepVerifier provides a declarative way of creating a verifiable script for an async Publisher sequence, by expressing expectations about the events that will happen upon subscription.
Still, I'm really curious, what way should be encouraged to use as the main road for doing testing in Reactor. Should .block()
method be abandoned completly or it could be useful in some cases? If yes, what such cases are?
Thanks!
from https://medium.com/swlh/stepverifier-vs-block-in-reactor-ca754b12846b
There are pros and cons of both
block()
andStepVerifier
testing patterns. Hence, it is necessary to define a pattern or set of rules which can guide us on how to use StepVerifier and block().In order to decide which patterns to use, we can try to answer the following questions which will provide a clear expectation from the tests we are going to write:
- Are we trying to test the reactive aspect of the code or just the output of the code?
- In which of the patterns we find clarity based on the 3 A’s of testing i.e Arrange, Act, and Assert, in order to make the test understandable?
- What are the limitations of the block() API over StepVerifier in testing reactive code? Which API is more fluent for writing tests in case of
Exception
?If you try answering all these questions above, you will find the answers to “what” and “where”. So, just give it a thought before reading the following answers:
- block() tests the output of the code and not the reactive aspect. In such a case where we are concerned about testing the output of the code, rather than the reactive aspect of the code we can use a block() instead of StepVerifier as it is easy to write and the tests
are more readable.- The assertion library for a block() pattern is better organised in terms of
3 A’s pattern
i.eArrange
,Act
, andAssert
than StepVerifier. In StepVerfier while testing a method call for a mock class or even while testing a Mono output one has to write expectation in the form of chained methods, unlike assert which in my opinion decreases the readability of the tests. Also, if you forget to write the terminal step i.everify()
in case of StepVerifier, the code will not get executed and the test will go green. So, the developer has to be very careful about calling verify at end of the chain.- There are some aspects of reactive code that can not be tested by using block() API. In such cases, one should use StepVerifier when we are testing a
Flux
of data or subscription delays or subscriptions on differentSchedulers
, etc, where the developer is bound to use StepVerifier.- To verify exception by using block() API you need to use
assertThatThrownBy
API in assertions library that catches the exception. With the use of an assertion API, error message and instance of the exception can be asserted. StepVerifier also provides assertions on exception byexpectError()
API and supports the
assertion of the element before errors are thrown in a Flux of elements that can not be achieved by block(). So, for the assertion of exception, StepVerifier is better than a block() as it can assert
bothMono/Flux
.