Using Spring mockMvc to test optional path variables

后端 未结 2 1971
猫巷女王i
猫巷女王i 2021-02-12 13:46

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.

Snippet from Controller,

2条回答
  •  自闭症患者
    2021-02-12 14:09

    Using an array of @RequestMapping values like this ...

    @RequestMapping(
        value = {"/some/uri/{foo}", "/some/uri/{foo}/{bar}"}, 
        method = RequestMethod.PUT)
    public ResponseEntity someMethod(
        @PathVariable("foo") String foo, 
        @PathVariable(value = "bar", required = false) String bar
    ) {
        return new ResponseEntity<>(foo + " and " + (bar == 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 ", mvcResult.getResponse().getContentAsString());
    
        mvcResult = mockMvc.perform(put("/some/uri/{foo}/{bar}", "foo", ""))
                .andExpect(status().isOk()).andReturn();
        Assert.assertEquals("foo and ", mvcResult.getResponse().getContentAsString());
    
        mvcResult = mockMvc.perform(put("/some/uri/{foo}", "foo"))
                .andExpect(status().isOk()).andReturn();
        Assert.assertEquals("foo and ", 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.

    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 ...

    private final AntPathMatcher antPathMatcher = new AntPathMatcher();
    
    @RequestMapping(value = "/some/uri/with/wildcards/**", method = RequestMethod.PUT)
    public ResponseEntity 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 ? "" : 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 ", mvcResult.getResponse().getContentAsString());
    
        mvcResult = mockMvc.perform(put("/some/uri/with/wildcards/{foo}/{bar}", "foo", ""))
                .andExpect(status().isOk()).andReturn();
        Assert.assertEquals("foo and ", mvcResult.getResponse().getContentAsString());
    
        mvcResult = mockMvc.perform(put("/some/uri/with/wildcards/{foo}", "foo"))
                .andExpect(status().isOk()).andReturn();
        Assert.assertEquals("foo and ", mvcResult.getResponse().getContentAsString());
    }
    

提交回复
热议问题