【问题标题】:Correct testing pattern in Project Reactor block() vs StepVerifierProject Reactor block() 与 StepVerifier 中的正确测试模式
【发布时间】:2020-07-02 13:10:06
【问题描述】:

最近我注意到我的团队在如何在 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();
}

以我的拙见,我会说第二种方法看起来更反应性。此外,官方文档对此非常清楚:

StepVerifier 提供了一种为异步发布者序列创建可验证脚本的声明方式,通过表达对订阅时将发生的事件的期望。

不过,我真的很好奇,应该鼓励使用哪种方式作为在 Reactor 中进行测试的主要途径。 .block() 方法应该完全放弃还是在某些情况下有用?如果有,这种情况是什么?

谢谢!

【问题讨论】:

  • StepVerifier 允许您验证链中的各个事件,而 block() 只允许您在最后验证结果。我想不出任何理由使用block() 而不是StepVerifier,但可以想到很多不这样做。
  • 在 Mono 的情况下,我并没有真正看到 StepVerifier 的任何优势。
  • @MartinTarjányi 众多优势。您可以验证它是否有 subscribes/errors/completes/onNext 事件。您可以验证事件是否在时间限制内发生。验证元素是否已通过过滤器正确丢弃。验证subscriberContext 的各个方面+ 更多。 Block() 实际上只是在最后给你一个命令式的值。无法测试链中实际发生的情况。
  • 这些对于 Flux 来说非常有用,但对于 Mono 来说却没有那么多。大多数情况下,我不需要这样的低级断言。

标签: unit-testing testing reactive-programming project-reactor


【解决方案1】:

您应该使用StepVerifier。它允许更多选择:

  • 验证您期望 n 元素在一个通量中
  • 验证通量/单声道完成
  • 验证预期会出现错误
  • 验证序列是预期的 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(); 
}

【讨论】:

    【解决方案2】:

    来自https://medium.com/swlh/stepverifier-vs-block-in-reactor-ca754b12846b

    block()StepVerifier 测试各有利弊 模式。因此,有必要定义一个模式或一组规则 这可以指导我们如何使用 StepVerifier 和 block()。

    为了决定使用哪种模式,我们可以尝试回答 以下问题将提供明确的期望 我们要编写的测试:

    • 我们是在尝试测试代码的反应方面还是仅测试代码的输出?
    • 我们根据 3 A 测试(即 Arrange、Act 和 Assert)找到清晰的模式,以便进行测试 可以理解吗?
    • block() API 相对于 StepVerifier 在测试响应式代码时有哪些限制?哪个 API 更流畅地编写测试 Exception的情况?

    如果您尝试回答以上所有这些问题,您会发现 回答“什么”和“在哪里”。所以,先考虑一下 阅读以下答案:

    • block() 测试代码的输出,而不是反应方面。在这种情况下,我们关心测试输出 代码,而不是代码的反应方面,我们可以使用 block() 而不是 StepVerifier,因为它易于编写和测试
      更具可读性。
    • block() 模式的断言库在3 A’s pattern (即ArrangeActAssert)方面组织得更好 步骤验证器。在 StepVerfier 中测试模拟的方法调用 上课,甚至在测试 Mono 输出时,必须写下期望 以链式方法的形式,不像我认为的断言 降低测试的可读性。另外,如果你忘记写 终端步骤,即 verify() 在 StepVerifier 的情况下,代码 不会被执行,测试会变绿。所以,开发商 在链的末端调用验证必须非常小心。
    • 反应式代码的某些方面无法使用 block() API 进行测试。在这种情况下,我们应该使用 StepVerifier 正在测试Flux 的数据或订阅延迟或订阅 在开发人员必须使用的不同Schedulers等上 StepVerifier。
    • 要使用 block() API 验证异常,您需要在断言库中使用 assertThatThrownBy API 例外。使用断言 API、错误消息和 可以断言异常的实例。 StepVerifier 还提供 expectError() API 的异常断言并支持
      在 Flux 中抛出错误之前元素的断言 block() 无法实现的元素。所以,对于断言 例外,StepVerifier 比 block() 更好,因为它可以断言
      都是Mono/Flux

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-06-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-01
      • 2020-09-14
      • 2019-04-06
      相关资源
      最近更新 更多