【问题标题】:Reactor switchIfEmpty and verifing an execution反应堆 switchIfEmpty 和验证执行
【发布时间】:2023-03-03 13:48:01
【问题描述】:

我有一个像这样的简单存储库实现。

@Repository
public interface PolicyRepository extends ReactiveMongoRepository<Policy, String> {

    @Query("{ id: { $exists: true }}")
    Flux<Policy> findAllPaged(Pageable pageable);
    @Query("{ name: { $eq: ?0 }}")
    Mono<Policy> findByName(String name);
}

还有一个像这样的控制器上的简单操作方法。

    @ResponseStatus(HttpStatus.CREATED)
    public Mono<ResponseEntity<String>> createPolicy(@Valid @RequestBody Policy policy) {
        //Use The Mongodb ID Generator
        policy.setId(ObjectId.get().toString());
        return policyRepository.findByName(policy.getName()).flatMap(policy1 -> {
            return Mono.just(ResponseEntity.badRequest().body("A Policy with the same name as the policy you are trying to create" +
                    "already exists"));
  }).switchIfEmpty(
          policyRepository.save(policy).map(p2 ->{
                    eventPublisher.publish(Events.POLICY_CREATED, p2.getId());
            return ResponseEntity.status(HttpStatus.CREATED).body("Policy definition created successfully");
                }));
    }

我想要实现的是,如果存在与插入的策略同名的策略,则返回错误请求;如果 findByName 方法返回空,则执行保存操作。

奇怪的行为是,下面的测试失败了,因为不管 findByName 是否返回数据,save 总是被调用。

这是测试

@Test
    void testCreateDuplicatePolicyShouldFail() {
        given(policyRepository.findByName(eq(policy.getName()))).willReturn(Mono.just(policy));
        given(policyRepository.save(any(Policy.class))).willReturn(Mono.just(policy));
        given(eventPublisher.publish(Events.POLICY_CREATED, policy.getId())).willReturn(Mono.just(0L));
        webTestClient.post().uri("/policies")
                .syncBody(policy)
                .exchange()
                .expectStatus().isBadRequest();
        verify(policyRepository, times(1)).findByName(eq(policy.getName()));
        verify(policyRepository, times(0)).save(any(Policy.class));
        verify(eventPublisher, times(0)).publish(Events.POLICY_CREATED, policy.getId());
    }

它失败并出现以下异常

org.mockito.exceptions.verification.NeverWantedButInvoked: 
com.management.dashboard.repository.PolicyRepository#0 bean.save(
    <any com.management.core.model.Policy>
);

请问我是不是做错了什么。任何指针将不胜感激。

【问题讨论】:

    标签: java spring-boot reactive-programming project-reactor java-10


    【解决方案1】:

    那个模拟设置的问题是save() IS 总是被调用。真实存储库返回的Mono 是惰性的,因此在订阅之前什么都不会发生。而switchIfEmpty 的工作是只有在没有收到onNext 信号时才进行所述订阅

    方法调用就是这样,一个方法调用。 switchIfEmpty 无法阻止 save 以这种形式执行。就好像你有类似 System.out.println(getMessage()): getMessage 的东西,只要整行被执行;)

    为了测试,你可以在模拟中使用reactor-testPublisherProbe

    @Test
    void testCreateDuplicatePolicyShouldFail() {
    
        //set up a probe to verify that the save Mono is never triggered
        PublisherProbe probe = PublisherProbe.of(Mono.just(policy));
        //now let the `save` return the probe:
        given(policyRepository.save(any(Policy.class))).willReturn(probe.mono());
    
        //rest of the mock and invocation is same
        given(policyRepository.findByName(eq(policy.getName()))).willReturn(Mono.just(policy));
        given(eventPublisher.publish(Events.POLICY_CREATED, policy.getId())).willReturn(Mono.just(0L));
        webTestClient.post().uri("/policies")
                .syncBody(policy)
                .exchange()
                .expectStatus().isBadRequest();
        verify(policyRepository, times(1)).findByName(eq(policy.getName()));
        verify(eventPublisher, times(0)).publish(Events.POLICY_CREATED, policy.getId());
    
        //but we now actually expect the save() to be invoked, but the probe to be inert:
        verify(policyRepository, times(1)).save(any(Policy.class));
        probe.assertWasNotSubscribed();
    
    }
    

    【讨论】:

    • 是否可以在反应器的文档中明确这一点?这很重要,可以使用一些细节。
    • 我不同意这属于反应堆文档。这几乎是标准的 java...maybeDoSomething("example")maybeDoSomething(produceString()) 是相同的,在第二种情况下,produceString() 总是被调用,无论maybeDoSomething 是否使用参数... Flux 的懒惰性质和Mono 在整个文档中重复
    【解决方案2】:

    您能否确认在测试中您设置的是空单声道。

    能否请您替换以下行:

    given(policyRepository.findByName(eq(policy.getName()))).willReturn(Mono.just(policy));
    

    用这一行:

    given(policyRepository.findByName(eq(policy.getName()))).willReturn(Mono.empty());
    

    switchIfEmpty 运算符仅在 Stream 为空时调用。 此外,您还可以启用日志来跟踪流程。这可以通过在 switchIfEmpty 之后添加日志运算符来完成。例如

     return policyRepository.findByName()
                            .switchIfEmpty()
                            .log();
    

    【讨论】:

    • 好的,会这样做的
    【解决方案3】:

    我遇到了同样的错误,它的解决方案是在 .switchIfEmpty() 函数中使用 Mono.defer(()-&gt;...),根据这篇 medium 文章,由于 switch if 预计将始终执行而没有副作用。

    【讨论】:

      猜你喜欢
      • 2020-01-10
      • 1970-01-01
      • 2018-04-13
      • 2022-01-13
      • 2013-08-18
      • 2021-05-02
      • 2020-05-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多