简体   繁体   中英

Unit test Springboot MockMvc returns 403 Forbidden

I wrote one unit test that tests UsersController. UsersControllerTest.findUser is working fine, but UsersControllerTest.insertGetModifyDelete it doesn't.

In the log of the test I can see that the POST request doesn't match any method of UsersController, but I don't understand why. Could you help me with this, please?.

This is my rest java class:

@RestController
@RequestMapping("/users")
public class UsersController {

    private final UsersService usersService;

    @Autowired
    public UsersController(UsersService usersService) {
        this.usersService = usersService;
    }

    @GetMapping(value="/{email}", produces="application/json")
    public User get(@PathVariable @Email String email) {
        return usersService.findByEmail(email);
    }

    @PostMapping(consumes="application/json", produces="application/json")
    @ResponseBody
    public ResponseEntity<String> insert(@RequestBody @Valid User user){
        usersService.insert(user);
        return ResponseEntity.ok(user.getEmail());
    }

    @DeleteMapping(value="/{email}", consumes="application/json", produces="application/json")
    public ResponseEntity<String> delete(@PathVariable @Email String email) {
        usersService.delete(email);
        return ResponseEntity.ok(email);
    }

    @PutMapping(value="/{email}", consumes="application/json", produces="application/json")
    public ResponseEntity<User> update(@PathVariable @Email String email, @RequestBody @Valid User user) {
        usersService.update(email, user);
        return ResponseEntity.ok(user);
    }

}

I have one test with 2 methods. One is working returning http code 200, but the other returns 403.

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
@WithMockUser(username = "user", roles = "USER")
public class UsersControllerTest {


    @Autowired
    private MockMvc mockMvc;

    @Test
    public void findUser() throws Exception {
        mockMvc.perform(get("/users/{email}", new Object[] {"boy@test.com"})).andExpect(status().isOk()).andExpect(jsonPath("$.email", equalTo("boy@test.com"))).andExpect(jsonPath("$.userName", equalTo("boy")));
    }

    @Test
    public void insertGetModifyDelete() throws Exception {
        User user = new User("userName", "email@email.com");
        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString(user);
        mockMvc.perform(post("/users").content(json).contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk());
        mockMvc.perform(put("/users/{email}", new Object[] {user.getEmail()}).content(json)).andDo(print()).andExpect(status().isOk());
        mockMvc.perform(delete("/users/{email}", new Object[] {user.getEmail()}).content(json)).andDo(print()).andExpect(status().isOk());
    }

}

And this is the output of the test:

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /users/boy@test.com
       Parameters = {}
          Headers = {}
             Body = null
    Session Attrs = {SPRING_SECURITY_CONTEXT=org.springframework.security.core.context.SecurityContextImpl@ca25360: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@ca25360: Principal: org.springframework.security.core.userdetails.User@36ebcb: Username: user; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_USER}

Handler:
             Type = es.tododev.fairtasks.rest.UsersController
           Method = public es.tododev.fairtasks.dto.User es.tododev.fairtasks.rest.UsersController.get(java.lang.String)

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

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

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = {Content-Disposition=[inline;filename=f.txt], Content-Type=[application/json;charset=UTF-8], X-Content-Type-Options=[nosniff], X-XSS-Protection=[1; mode=block], Cache-Control=[no-cache, no-store, max-age=0, must-revalidate], Pragma=[no-cache], Expires=[0], X-Frame-Options=[DENY]}
     Content type = application/json;charset=UTF-8
             Body = {"userName":"boy","email":"boy@test.com"}
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

MockHttpServletRequest:
      HTTP Method = POST
      Request URI = /users
       Parameters = {}
          Headers = {Content-Type=[application/json;charset=UTF-8], Accept=[application/json]}
             Body = {"userName":"userName","email":"email@email.com"}
    Session Attrs = {org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository.CSRF_TOKEN=org.springframework.security.web.csrf.DefaultCsrfToken@66944c7c, SPRING_SECURITY_CONTEXT=org.springframework.security.core.context.SecurityContextImpl@ca25360: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@ca25360: Principal: org.springframework.security.core.userdetails.User@36ebcb: Username: user; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_USER}

Handler:
             Type = null

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

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

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 403
    Error message = Forbidden
          Headers = {X-Content-Type-Options=[nosniff], X-XSS-Protection=[1; mode=block], Cache-Control=[no-cache, no-store, max-age=0, must-revalidate], Pragma=[no-cache], Expires=[0], X-Frame-Options=[DENY]}
     Content type = null
             Body =
    Forwarded URL = null
   Redirected URL = null
          Cookies = []
[ERROR] Tests run: 2, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 6.155 s <<< FAILURE! - in es.tododev.fairtasks.rest.UsersControllerTest
[ERROR] insertGetModifyDelete(es.tododev.fairtasks.rest.UsersControllerTest)  Time elapsed: 0.028 s  <<< FAILURE!
java.lang.AssertionError: Status expected:<200> but was:<403>
        at es.tododev.fairtasks.rest.UsersControllerTest.insertGetModifyDelete(UsersControllerTest.java:48)

I got around it with @AutoConfigureMockMvc(addFilters = false) . Please make sure that this won't compromise other functionality on your HTTP stack.

You can try to debug this program.i think probleam is happend in "mockMvc" object is not autowired.mockMvc object should load from WebApplicationContext in before program run.

@Autowired
private WebApplicationContext webApplicationContext

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

When working with CSRF protection, a CSRF token must be provided or a 403 status will be returned. I fixed this in my testing by specifically adding a CSRF token to any non-safe HTTP method (POST, PUT, DELETE) as per the documentation .

import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;

[...]

mvc.perform(post("/").with(csrf()));
mvc.perform(put("/").with(csrf()));
mvc.perform(delete("/").with(csrf()));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM