Project Reactor block() 与 StepVerifier 中的正确测试模式
Correct testing pattern in Project Reactor block() vs StepVerifier
最近我注意到我的团队遵循两种方法来在 Reactor 中编写测试。第一个是借助 .block()
方法。它看起来像这样:
@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());
}
第二个是关于StepVerifier
的使用。它看起来像这样:
@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();
}
以我的拙见,第二种方法看起来更 反应性 我会说。此外,官方文档对此非常清楚:
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.
不过,我真的很好奇,应该鼓励使用什么方式作为在 Reactor 中进行测试的主要途径。 .block()
方法应该完全放弃还是在某些情况下有用?如果是,这些情况是什么?
谢谢!
你应该使用 StepVerifier。它允许更多选项:
- 验证您是否期望通量中有 n 个元素
- 验证 flux/mono 完成
- 验证是否会出现错误
- 验证序列是否为 n 个元素后跟错误(无法使用 .block() 进行测试)
来自官方文档:
public <T> Flux<T> appendBoomError(Flux<T> source) {
return source.concatWith(Mono.error(new IllegalArgumentException("boom")));
}
@Test
public void testAppendBoomError() {
Flux<String> source = Flux.just("thing1", "thing2");
StepVerifier.create(
appendBoomError(source))
.expectNext("thing1")
.expectNext("thing2")
.expectErrorMessage("boom")
.verify();
}
- 创建initial context
- Using virtual time to manipulate time。因此,当您有类似 Mono.delay(Duration.ofDays(1)) 的内容时,您无需等待 1 天即可完成测试。
- 预计在给定的持续时间内不会发出任何事件...
来自 https://medium.com/swlh/stepverifier-vs-block-in-reactor-ca754b12846b
There are pros and cons of both block()
and StepVerifier
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.e Arrange
, Act
, and Assert
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.e verify()
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 different Schedulers
, 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 by expectError()
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
both Mono/Flux
.
最近我注意到我的团队遵循两种方法来在 Reactor 中编写测试。第一个是借助 .block()
方法。它看起来像这样:
@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());
}
第二个是关于StepVerifier
的使用。它看起来像这样:
@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();
}
以我的拙见,第二种方法看起来更 反应性 我会说。此外,官方文档对此非常清楚:
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.
不过,我真的很好奇,应该鼓励使用什么方式作为在 Reactor 中进行测试的主要途径。 .block()
方法应该完全放弃还是在某些情况下有用?如果是,这些情况是什么?
谢谢!
你应该使用 StepVerifier。它允许更多选项:
- 验证您是否期望通量中有 n 个元素
- 验证 flux/mono 完成
- 验证是否会出现错误
- 验证序列是否为 n 个元素后跟错误(无法使用 .block() 进行测试)
来自官方文档:
public <T> Flux<T> appendBoomError(Flux<T> source) {
return source.concatWith(Mono.error(new IllegalArgumentException("boom")));
}
@Test
public void testAppendBoomError() {
Flux<String> source = Flux.just("thing1", "thing2");
StepVerifier.create(
appendBoomError(source))
.expectNext("thing1")
.expectNext("thing2")
.expectErrorMessage("boom")
.verify();
}
- 创建initial context
- Using virtual time to manipulate time。因此,当您有类似 Mono.delay(Duration.ofDays(1)) 的内容时,您无需等待 1 天即可完成测试。
- 预计在给定的持续时间内不会发出任何事件...
来自 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
.