如何测试DeferredResult超时结果

问题描述 投票:0回答:2

我正在按照不久前的 Spring 博客实现长轮询

这里我转换后的方法具有与以前相同的响应签名,但它现在使用长轮询,而不是立即响应:

private Map<String, DeferredResult<ResponseEntity<?>>> requests = new ConcurrentHashMap<>();

@RequestMapping(value = "/{uuid}", method = RequestMethod.GET)
public DeferredResult<ResponseEntity<?>> poll(@PathVariable("uuid") final String uuid) {
    // Create & store a new instance
    ResponseEntity<?> pendingOnTimeout = ResponseEntity.accepted().build();
    DeferredResult<ResponseEntity<?>> deferredResult = new DeferredResult<>(TWENTYFIVE_SECONDS, pendingOnTimeout);
    requests.put(uuid, deferredResult);

    // Clean up poll requests when done
    deferredResult.onCompletion(() -> {
        requests.remove(deferredResult);
    });

    // Set result if already available
    Task task = taskHolder.retrieve(uuid);
    if (task == null)
        deferredResult.setResult(ResponseEntity.status(HttpStatus.GONE).build());
    else
        // Done (or canceled): Redirect to retrieve file contents
        if (task.getFutureFile().isDone())
            deferredResult.setResult(ResponseEntity.created(RetrieveController.uri(uuid)).build());

    // Return result
    return deferredResult;
}

特别是,当请求花费太长时间时,我想返回

pendingOnTimeout
响应(我之前立即返回过),以防止代理切断请求。

现在我认为我已经按原样工作了,但我想编写一个单元测试来证实这一点。然而,我使用 MockMvc (通过 webAppContextSetup)的所有尝试都未能为我提供一种断言我获得

accepted
标头的方法。例如,当我尝试以下操作时:

@Test
public void pollPending() throws Exception {
    MvcResult result = mockMvc.perform(get("/poll/{uuid}", uuidPending)).andReturn();
    mockMvc.perform(asyncDispatch(result))
            .andExpect(status().isAccepted());
}

我得到以下堆栈跟踪:

java.lang.IllegalStateException:处理程序的异步结果 [public org.springframework.web.context.request.async.DeferredResult> nl.bioprodict.blast.api.PollController.poll(java.lang.String)] 期间未设置指定的 timeToWait=25000 在 org.springframework.util.Assert.state(Assert.java:392) 在 org.springframework.test.web.servlet.DefaultMvcResult.getAsyncResult(DefaultMvcResult.java:143) 在 org.springframework.test.web.servlet.DefaultMvcResult.getAsyncResult(DefaultMvcResult.java:120) 在 org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch(MockMvcRequestBuilders.java:235) 在nl.bioprodict.blast.docs.PollControllerDocumentation.pollPending(PollControllerDocumentation.java:53) ...

与此相关的 Spring 框架测试似乎都使用了模拟: https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/test/java/org /springframework/web/context/request/async/WebAsyncManagerTimeoutTests.java

如何测试 DeferredResult timeoutResult 的正确处理?

spring-mvc spring-mvc-test spring-web
2个回答
14
投票

就我而言,在查看 Spring 源代码并设置超时(10000 毫秒)并获取异步结果后,为我解决了这个问题,如下;

     mvcResult.getRequest().getAsyncContext().setTimeout(10000);
     mvcResult.getAsyncResult();

// My whole test code was;

    
    MvcResult mvcResult = this.mockMvc.perform(
                                    post("<SOME_RELATIVE_URL>")
                                    .contentType(MediaType.APPLICATION_JSON)
                                    .content(<JSON_DATA>))
                                .andExpect(request().asyncStarted())***
                                .andReturn();
    
    ***mvcResult.getRequest().getAsyncContext().setTimeout(10000);***
    ***mvcResult.getAsyncResult();***
    
    this.mockMvc
        .perform(asyncDispatch(mvcResult))
        .andDo(print())
        .andExpect(status().isOk());

希望有帮助..


6
投票

使用Spring 4.3遇到了这个问题,并设法找到了一种在单元测试中触发超时回调的方法。获取

MvcResult
后,在调用
asyncDispatch()
之前,可以插入如下代码:

MockAsyncContext ctx = (MockAsyncContext) mvcResult.getRequest().getAsyncContext();
for (AsyncListener listener : ctx.getListeners()) {
    listener.onTimeout(null);
}

请求的异步侦听器之一将调用

DeferredResult
的超时回调。

所以你的单元测试将如下所示:

@Test
public void pollPending() throws Exception {
    MvcResult result = mockMvc.perform(get("/poll/{uuid}", uuidPending)).andReturn();
    MockAsyncContext ctx = (MockAsyncContext) result.getRequest().getAsyncContext();
    for (AsyncListener listener : ctx.getListeners()) {
        listener.onTimeout(null);
    }
    mockMvc.perform(asyncDispatch(result))
            .andExpect(status().isAccepted());
}
© www.soinside.com 2019 - 2024. All rights reserved.