Java 使用 Spring mockMvc 测试可选路径变量

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/45825955/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 02:10:56  来源:igfitidea点击:

Using Spring mockMvc to test optional path variables

javaspringspring-mvcspring-testspring-test-mvc

提问by bernard

I have a method in Spring MVC with optional path variable. I am trying to test it for a scenario when optional path variable is not provided.

我在 Spring MVC 中有一个带有可选路径变量的方法。我正在尝试针对未提供可选路径变量的场景对其进行测试。

Snippet from Controller, resource URI to invoke-

来自控制器的片段,要调用的资源 URI-

@RequestMapping(value = "/some/uri/{foo}/{bar}", method = RequestMethod.PUT)
public <T> ResponseEntity<T> someMethod(@PathVariable("foo") String foo, @PathVariable(value = "bar", required = false) String bar) {

    LOGGER.info("foo: {}, bar: {}", foo, bar);
}

Snippet from my test using MockMvc-

我使用 MockMvc 测试的片段-

//inject context
@Autowired
private WebApplicationContext webApplicationContext;

protected MockMvc mockMvc;

@Before
public void setup() {
    //build mockMvc
    mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}

@Test
public void someMethodTest() throws Exception {
    //works as expected
    mockMvc.perform(put("/some/uri/{foo}/{bar}", "foo", "bar"))
            .andExpect(status().isOk()); //works

    //following doesn't work

    //pass null for optional
    mockMvc.perform(put("/some/uri/{foo}/{bar}", "foo", null))
            .andExpect(status().isOk()); //throws 404

    //pass empty for optional
    mockMvc.perform(put("/some/uri/{foo}/{bar}", "foo", ""))
            .andExpect(status().isOk()); //throws 404

    //remove optional from URI
    mockMvc.perform(put("/some/uri/{foo}", "foo"))
            .andExpect(status().isOk()); //throws 404
}

采纳答案by glytching

Using an array of @RequestMappingvalues like this ...

使用这样的@RequestMapping值数组......

@RequestMapping(value = {"/some/uri/{foo}", "/some/uri/{foo}/{bar}"}, method = RequestMethod.PUT)
public ResponseEntity<String> someMethod(@PathVariable("foo") String foo, @PathVariable(value = "bar", required = false) String bar) {
    return new ResponseEntity<>(foo + " and " + (bar == null ? "<null>" : bar), HttpStatus.OK);
}

... will enable this test to pass:

...将使此测试通过:

@Test
public void someMethodTest() throws Exception {
    MvcResult mvcResult = mockMvc.perform(put("/some/uri/{foo}/{bar}", "foo", "bar"))
            .andExpect(status().isOk()).andReturn();
    Assert.assertEquals("foo and bar", mvcResult.getResponse().getContentAsString());

    mvcResult = mockMvc.perform(put("/some/uri/{foo}/{bar}", "foo", null))
            .andExpect(status().isOk()).andReturn();
    Assert.assertEquals("foo and <null>", mvcResult.getResponse().getContentAsString());

    mvcResult = mockMvc.perform(put("/some/uri/{foo}/{bar}", "foo", ""))
            .andExpect(status().isOk()).andReturn();
    Assert.assertEquals("foo and <null>", mvcResult.getResponse().getContentAsString());

    mvcResult = mockMvc.perform(put("/some/uri/{foo}", "foo"))
            .andExpect(status().isOk()).andReturn();
    Assert.assertEquals("foo and <null>", mvcResult.getResponse().getContentAsString());
}

That certainly seems to be the simplest solution and it is likely to be more friendly to tools such as Swagger since it make the mappings explicit.

这当然似乎是最简单的解决方案,它可能对 Swagger 等工具更友好,因为它使映射变得明确。

However, you could also declare a wildcard mapping and then use a path matcher within your controller method to interpret the request URI. For example, this method ...

但是,您也可以声明通配符映射,然后在控制器方法中使用路径匹配器来解释请求 URI。比如这个方法...

private final AntPathMatcher antPathMatcher = new AntPathMatcher();

@RequestMapping(value = "/some/uri/with/wildcards/**", method = RequestMethod.PUT)
public ResponseEntity<String> someMethod(HttpServletRequest request) {
    String matched = antPathMatcher.extractPathWithinPattern(
            (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE), request.getPathInfo());
    // ugly parsing code to read the path variables, allowing for the optionality of the second one
    String foo = matched;
    String bar = null;
    String[] pathVariables = matched.split("/");
    if (pathVariables.length > 1) {
        foo = pathVariables[0];
        bar = pathVariables[1];
    }
    return new ResponseEntity<>(foo + " and " + (bar == null ? "<null>" : bar), HttpStatus.OK);
}

... will enable this test to pass:

...将使此测试通过:

@Test
public void someMethodTestWithWildcards() throws Exception {
    MvcResult mvcResult = mockMvc.perform(put("/some/uri/with/wildcards/{foo}/{bar}", "foo", "bar"))
            .andExpect(status().isOk()).andReturn();
    Assert.assertEquals("foo and bar", mvcResult.getResponse().getContentAsString());

    mvcResult = mockMvc.perform(put("/some/uri/with/wildcards/{foo}/{bar}", "foo", null))
            .andExpect(status().isOk()).andReturn();
    Assert.assertEquals("foo and <null>", mvcResult.getResponse().getContentAsString());

    mvcResult = mockMvc.perform(put("/some/uri/with/wildcards/{foo}/{bar}", "foo", ""))
            .andExpect(status().isOk()).andReturn();
    Assert.assertEquals("foo and <null>", mvcResult.getResponse().getContentAsString());

    mvcResult = mockMvc.perform(put("/some/uri/with/wildcards/{foo}", "foo"))
            .andExpect(status().isOk()).andReturn();
    Assert.assertEquals("foo and <null>", mvcResult.getResponse().getContentAsString());
}

回答by Manoj Kumar S

This is late but I faced recently this situation and thought this post would help others.

这已经很晚了,但我最近遇到了这种情况,并认为这篇文章会对其他人有所帮助。

In case of mocking endpoints with optional request param or path variable, you can specify it like this.

如果使用可选的请求参数或路径变量模拟端点,您可以像这样指定它。

Say I have a method with params as m1(String param1, String param2)called from controller.

假设我有一个m1(String param1, String param2)从控制器调用的带有参数的方法。

Where param 2 is an optional param for controller, so at runtime null would be passed if it is not passed.

其中 param 2 是控制器的可选参数,因此在运行时如果未传递 null 则将传递它。

How to mock:

如何嘲讽:

Mockito.when(m1(Mockito.anyString(), Mockito.eq(null)).the return(<whatever you want to return>)

Use Mockito.eq(null)in your test to pass it as null for optional param.

Mockito.eq(null)在您的测试中使用以将其作为 null 传递给可选参数。