Search code examples
spring-bootjunitspring-mvc-testspring-boot-test

How to configure HandlerMethodArgumentResolver in spring boot test


I am writing a unit for a controller with spting boot @WebMvcTest.

Using @WebMvcTest, I will be able to inject a MockMvc object as given below :-

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {TestConfig.class})
@WebMvcTest
class MyControllerTest {
    @Autowired
    private MockMvc mockMvc;

    @Test
    public void my_controller_test() throws Exception {
       mockMvc.perform(post("/create-user"))
              .andExpect(status().isCreated());
    }
}

In the controller I am injecting a Principal argument using spring HandlerMethodArgumentResolver. Please inform me how can I write a unit test with MockMvc , so that I can inject a mock Principal object as the argument in the controller method.

The sction Auto-configured Spring MVC tests explains that the Test annotated with @WebMvcTest will scan beans of HandlerMethodArgumentResolver. So I created a bean which extends HandlerMethodArgumentResolver and return the mock Principal object as below .

@Component
public class MockPrincipalArgumentResolver implements HandlerMethodArgumentResolver {
   @Override
   public boolean supportsParameter(MethodParameter parameter) {
     return parameter.getParameterType().equals(Principal.class);
   }

   @Override
   public Object resolveArgument(MethodParameter parameter...) throws Exception {
     return new MockPrincipal();
   }
 }

But still the Argument MockPrincipal is not getting passed to controller method.

Spring boot version :- 1.4.5.RELEASE


Solution

  • You are using MockMvc to call your controller. With that you have to prepare the request with things like parameters, body, URL and also the principal. What you don't specify won't be included (you are now basically doing a call without an authenticated principal).

    The Spring MVC Testing support for MockMvc is documented, in general, in the reference guide.

    For more detailed info check the component that is used for building a mock request the MockHttpServletRequestBuilder. Which is what your post method will return, which should be a call the MockHttpServletRequestBuilders.post (and probably a static import in your code). A [CTRL]+[SPACE] (or whatever your favorite code completion short cut is in your iDE) after post() will give you some insight in what is available.

    @Test
    public void my_controller_test() throws Exception {
       mockMvc.perform(post("/create-user").principal(new MockPrincipal())
              .andExpect(status().isCreated());
    }
    

    Something like the above should do the trick.