15

我正在使用一个安静的 url 来启动一个长时间运行的后端进程(它通常在一个 cron 计划中,但我们希望能够手动启动它)。

下面的代码有效,当我手动测试时,我会在浏览器中看到结果。

@ResponseBody
@RequestMapping(value = "/trigger/{jobName}", method = RequestMethod.GET)
public Callable<TriggerResult> triggerJob(@PathVariable final String jobName) {

    return new Callable<TriggerResult>() {
        @Override
        public TriggerResult call() throws Exception {
            // Code goes here to locate relevant job and kick it off, waiting for result
            String message = <result from my job>;
            return new TriggerResult(SUCCESS, message);
        }
    };
}

当我在没有Callable使用以下代码的情况下进行测试时,一切正常(我更改了预期的错误消息以简化帖子)。

mockMvc.perform(get("/trigger/job/xyz"))
    .andExpect(status().isOk())
    .andDo(print())
    .andExpect(jsonPath("status").value("SUCCESS"))
    .andExpect(jsonPath("message").value("A meaningful message appears"));

当我添加Callable但是它不起作用。我也在下面尝试过,但没有奏效。还有人成功吗?

mockMvc.perform(get("/trigger/job/xyz"))
    .andExpect(status().isOk())
    .andDo(print())
    .andExpect(request().asyncResult(jsonPath("status").value("SUCCESS")))
    .andExpect(request().asyncResult(jsonPath("message").value("A meaningful message appears")));

以下是我的 print() 中的相关部分。在这种情况下,mockMvc 似乎无法正确解开 Json(即使它在我的浏览器中工作)?当我在没有Callable看到完整 JSON 的情况下执行此操作时。

MockHttpServletRequest:
     HTTP Method = GET
     Request URI = /trigger/job/xyz
      Parameters = {}
         Headers = {}

         Handler:
            Type = foo.bar.web.controller.TriggerJobController
          Method = public java.util.concurrent.Callable<foo.bar.myproject.web.model.TriggerResult> foo.bar.myproject.web.controller.TriggerJobController.triggerJob(java.lang.String)

           Async:
 Was async started = true
      Async result = foo.bar.myproject.web.model.TriggerResult@67aa1e71


Resolved Exception:
            Type = null

    ModelAndView:
       View name = null
            View = null
           Model = null

        FlashMap:

MockHttpServletResponse:
          Status = 200
   Error message = null
         Headers = {}
    Content type = null
            Body = 
   Forwarded URL = null
  Redirected URL = null
         Cookies = []
4

3 回答 3

21

Bud 的回答确实帮助我指出了正确的方向,但是它并没有完全起作用,因为它没有等待异步结果。自发布此问题以来,spring-mvc-showcase 示例 ( https://github.com/SpringSource/spring-mvc-showcase ) 已更新。

似乎在检索 MvcResult 时调用的第一部分,您需要在 asyncResult() 上进行断言,而在 JSON pojo 映射的情况下,您需要在实际类型本身(而不是 JSON)上进行断言。所以我需要将下面的第三行添加到 Bud 的答案中,然后剩下的就可以了。

MvcResult mvcResult = this.mockMvc.perform(get("/trigger/job/xyz"))
    .andExpect(request().asyncStarted())
    .andExpect(request().asyncResult(instanceOf(TriggerResult.class)))
    .andReturn();

this.mockMvc.perform(asyncDispatch(mvcResult))
    .andExpect(status().isOk())
    .andExpect(content().contentType(MediaType.APPLICATION_JSON))
    .andExpect(jsonPath("status").value("SUCCESS"))
    .andExpect(jsonPath("message").value("A meaningful message appears"));

注: instanceOf()org.hamcrest.CoreMatchers.instanceOf。要访问 Hamcrest 库,请包含最新的hamcrest-libraryjar。

对于行家...

    <dependency>
        <groupId>org.hamcrest</groupId>
        <artifactId>hamcrest-library</artifactId>
        <version>LATEST VERSION HERE</version>
        <scope>test</scope>
    </dependency>
于 2013-07-29T04:08:55.887 回答
11

马特的回答是正确的,但我perform只想工作。下面是一个 perform 方法,可用于测试异步和同步请求。因此,您无需在测试中关心后端如何处理请求。无论如何,您只对实际响应感兴趣,对吗?

ResultActions perform(MockHttpServletRequestBuilder builder) throws Exception {
    ResultActions resultActions = mockMvc.perform(builder);
    if (resultActions.andReturn().getRequest().isAsyncStarted()) {
      return mockMvc.perform(asyncDispatch(resultActions
          .andExpect(request().asyncResult(anything()))
          .andReturn()));
    } else {
      return resultActions;
    }
}

将它集成到您​​的测试中的一种方法是将它放在一个通用的抽象基类中并从中扩展您的实际测试类:

import static org.hamcrest.Matchers.anything;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;

@WebAppConfiguration
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml")
public abstract class AbstractMockMvcTests {

  @Autowired
  protected WebApplicationContext wac;

  private MockMvc mockMvc;

  @Before
  public void setup() throws Exception {
    mockMvc = webAppContextSetup(this.wac).build();
  }

  protected ResultActions perform(MockHttpServletRequestBuilder builder) throws Exception {
    ResultActions resultActions = mockMvc.perform(builder);
    if (resultActions.andReturn().getRequest().isAsyncStarted()) {
      return mockMvc.perform(asyncDispatch(resultActions
          .andExpect(request().asyncResult(anything()))
          .andReturn()));
    } else {
      return resultActions;
    }
  }
}

然后通过扩展基类并使用 perform 方法来实现您的测试。在这个示例中,mockMvc 被设为私有,以温和地指导所有未来的测试作者使用自定义 perform 方法。

@RunWith(SpringJUnit4ClassRunner.class)
public class CallableControllerTests extends AbstractMockMvcTests {

  @Test
  public void responseBodyAsync() throws Exception {
    perform(get("/async/callable/response-body"))
      .andExpect(status().isOk())
      .andExpect(content().contentType("text/plain;charset=ISO-8859-1"))
      .andExpect(content().string("Callable result"));
  }

  @Test
  public void responseBodySync() throws Exception {
    perform(get("/sync/foobar/response-body"))
      .andExpect(status().isOk())
      .andExpect(content().contentType("text/plain;charset=ISO-8859-1"))
      .andExpect(content().string("Sync result"));
  }
}
于 2016-02-28T17:00:57.567 回答
7

我认为您想对已启动的异步调用的结果使用 asyncDispatch 来自下面链接的参考代码

http://static.springsource.org/spring/docs/3.2.x/javadoc-api/org/springframework/test/web/servlet/request/MockMvcRequestBuilders.html

用法涉及首先执行一个启动异步处理的请求:

 MvcResult mvcResult = this.mockMvc.perform(get("/trigger/job/xyz"))
        .andExpect(request().asyncStarted())
        .andReturn();

然后重新使用 MvcResult 执行异步调度:

 this.mockMvc.perform(asyncDispatch(mvcResult))
        .andExpect(status().isOk())
        .andExpect(content().contentType(MediaType.APPLICATION_JSON))
        .andExpect(content().string(.......));

或者在你的情况下

this.mockMvc.perform(asyncDispatch(mvcResult))
        .andExpect(status().isOk())
        .andExpect(content().contentType(MediaType.APPLICATION_JSON))
        .andExpect(jsonPath("status").value("SUCCESS"))
        .andExpect(jsonPath("message").value("A meaningful message appears"));
于 2013-07-19T01:27:40.363 回答