Webflux单元测试:调用未进入.map和.flatMap。

huangapple go评论76阅读模式
英文:

Webflux unit testing: call is not going inside .map and .flatMap

问题

我正在为webflux中的calculate函数编写单元测试。

以下是测试用例:

在运行测试用例时,即使dataFromRedis不为空,调用也不会进入.map、.flatMap和.map。有什么我遗漏的吗?

英文:

I am writing unit testing for function calculate in webflux

@Override
    public Mono<SearchSessionAndETAResponse> calculate(DTO sessionRequest) throws ExecutionException, InterruptedException {
        List<GeospatialData> dataFromRedis =  getDataFromRedis(sessionRequest.getSessionId());
        return Mono.just(dataFromRedis)
                .map(data -> {
                    System.out.println("data::"+data);
                    return "entityIdentifiers";
                })
                .flatMap(entityIdentifiers -> {
                    System.out.println("entityIdentifiers::"+entityIdentifiers);
                    return etaService.entitiesEta("entityIdentifiers");
                })
                .map(wayPointRoute -> {
                    return searchSessionAndETAResponse;
                });
    }
    

below is the test case

@ExtendWith(MockitoExtension.class)
public class BroadCastingServiceImplTest {

    @InjectMocks
    private BroadCastingServiceImpl broadCastingService;
    @Mock
    private EtaService etaService;

   @BeforeEach
    void setUp() {
        closeable = MockitoAnnotations.openMocks(this);
    }

    @AfterEach
    void tearDown() throws Exception {
        closeable.close();
    }

@Test
    @DisplayName("Given a valid request, when SearchSession and calculate eta is called, it should search Broadcasting session and return eta")
    void givenValidRequest_whenSearchSessionAndCalculateEta_shouldReturnBroadCastingResponseDTO() throws ExecutionException, InterruptedException, IOException {
        // GIVEN
        UUID sessionId = UUID.randomUUID();
        DTO searchSessionAndCalculateETA = new DTO();
        searchSessionAndCalculateETA.setSessionId(sessionId.toString());
        searchSessionAndCalculateETA.setWaypoints(List.of("waypointsDTO", "waypointsDTO2"));
        given(etaService.entitiesEta("entitiesEtaRequestDTO")).willReturn(Mono.just(List.of("entitiesEtaResponseDTO")));

        // WHEN
        Mono<SearchSessionAndETAResponse> result = broadCastingService.searchSessionAndCalculateETA(searchSessionAndCalculateETA);
    }
}

The problem:

when i run test case the call does not go inside .map, .flatMap and .map even though dataFromRedis is not null.
Is there anything i am missing ?

答案1

得分: 1

在你的测试中,你只是创建了 Mono,但实际上并没有真正地"运行"它,更准确地说,你没有订阅它。

与响应式代码一起工作有两个阶段:

  1. 首先,你编写响应式流的规范,告诉它要做什么,但不会启动过程。Mono 只是一个带有许多 lambda 函数的复杂内存结构,告诉它什么时候做什么。

  2. 然后,你订阅 Mono - 只有这样才会启动整个过程,并开始调用所有的 lambda。只有在这时,"调用进入 .map 和 .flatMap",用你的话来说 Webflux单元测试:调用未进入.map和.flatMap。

阅读一些教程,例如 Baeldung 的 Reactor Core 入门

如果你只是在简单的 Spring 应用程序中使用 Mono 来处理 REST 端点,你可能会轻易忽略这一点,因为你的 @RestController 只返回 Mono,其余的工作由 Spring 处理。Spring 订阅 了你从控制器方法返回的 Mono,并确保将结果传递给通过 HTTP 调用的对象。

单元测试中,你可以通过调用其 block() 方法来非常简单地执行,或者更正确地使用 StepVerifier

result.as(StepVerifier::create)
    .expectNext(objectToExpect)
    .verifyComplete();

只使用 block() 方法更像是一种权宜之计。StepVerifier 是正确的单元测试响应式代码的方式。

要小心不要仅仅使用 subscribe() 方法,因为测试可能在订阅有时间执行之前结束,从而导致不可预测的结果。

英文:

In your test you just create the Mono but you don't actually "run" it, more precisely you do not subscribe to it.

Working with reactive code has two phases:

  1. First you write the specification of the reactive stream, which tells what to do, but doesn't start the process. A Mono is only a fancy complex memory structure with a lot of lambda functions telling what will be done when it will be done.

  2. Then you subscribe to the Mono - only this starts the whole process and starts calling all the lambdas. Only then the "call is going inside .map and .flatMap" to paraphrase your words Webflux单元测试:调用未进入.map和.flatMap。

Read some tutorial, e.g. this Baeldung's Intro To Reactor Core.

If you just use Mono in simple Spring applications to handle REST endpoint, you may easily miss this because your @RestController just returns the Mono and the rest (pun intended) is handled by Spring. Spring subscribes to the Mono you returned from the controller methods and handles that the result is passed to the called via HTTP.

In unit tests, you can do it very simply just by calling its block() method, or more properly by using StepVerifier:

result.as(StepVerifier::create)
    .expectNext(objectToExpect)
    .verifyComplete();

Just using the block() method works more like a workaround. StepVerifier is the proper way of unit testing the reactive code.

Be careful not to just use the subscribe() method, because the test may finish before the subscription has time to perform and you will get unpredictable results.

huangapple
  • 本文由 发表于 2023年6月26日 18:56:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/76556045.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定