Home > Mobile >  Junit5: WebMvcTest returns 404. Probably because I'm not mocking underlyng method?
Junit5: WebMvcTest returns 404. Probably because I'm not mocking underlyng method?

Time:02-13

Preamble: I'm learning Java, Spring Boot and overall... TDD with Java/Spring Boot.

Used versions:

  • Spring Boot 2.6.3
  • Java 17
  • Junit5

This is my controller:

@RestController
@RequestMapping("/api/v1/login")
public class LoginController {

    @Autowired
    private JwtAuthentication jwtAuthentication;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private JwtUserDetailService jwtUserDetailService;

    @PostMapping
    public ResponseEntity<?> createAuthenticationToken(@RequestBody UserEntity userEntity) throws Exception {
        jwtAuthentication.authenticate(userEntity.getUsername(), userEntity.getPassword());
        final UserDetails userDetails = jwtUserDetailService.loadUserByUsername(userEntity.getUsername());
        final String token = jwtTokenUtil.generateToken(userDetails);
        return ResponseEntity.ok(new JwtResponse(token));
    }
}

Relevant autowired is the JwtAuthentication:

@Component
public class JwtAuthentication {

    @Autowired
    private AuthenticationManager authenticationManager;

    private static final long serialVersionUID = -20220210203900L;

    public void authenticate(String username, String password) throws BadCredentialsException {
        try {
            authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
        } catch (BadCredentialsException e) {
            throw new BadCredentialsException("INVALID_CREDENTIALS", e);
        }
    }
}

I wrote the test for JwtAuthentication itself without issues, now I need to test the Controller.

This is my test:

@ExtendWith(SpringExtension.class)
@WebMvcTest(LoginControllerTest.class)
class LoginControllerTest {

    @Autowired
    private MockMvc mvc;

    @Autowired
    private ObjectMapper objectMapper;
    
    @MockBean
    private JwtAuthentication jwtAuthentication;
    
    
    @Test
    void testCanLogin() throws Exception {

        UserEntity userEntity = new UserEntity();
        userEntity.setUsername("username");
        userEntity.setPassword("password");
        
        mvc.perform(post("/api/v1/login/").contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsBytes(userEntity))).andExpect(status().isOk());
    }
}

But I get 404 instead of 200.

I read that reason is missing mocking underlying methods. That, in reality, these tests on Controller doesn't launch entire configuration (and so on). Cannot find the answer atm, here on S.O..

So, I think the solution need to be add a "simple" when in test:

@Test
void testCanLogin() throws Exception {

UserEntity userEntity = new UserEntity();
userEntity.setUsername("username");
userEntity.setPassword("password");

// I think I need some code here:
// pseudocode    when(jwtAuthentication.authenticate("username", "password").then .... I DON'T KNOW HERE!

mvc.perform(post("/api/v1/login/").contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsBytes(userEntity))).andExpect(status().isOk());
    }

CodePudding user response:

404 is because of no controllers to handle the request.

It is because you specify a wrong controller in @WebMvcTest such that the controller that you want to test is not included in the spring container. So change to the following should fix the problem :

@WebMvcTest(LoginController.class)
class LoginControllerTest {

    @MockBean
    private JwtAuthentication jwtAuthentication;

    @MockBean
    private JwtTokenUtil jwtTokenUtil;

    @MockBean
    private JwtUserDetailService jwtUserDetailService;

}

Also note the following points :

  • I remove @ExtendWith(SpringExtension.class) as @WebMvcTest already included it
  • @WebMvcTest will only enable the beans related to web layers (see this) which JwtTokenUtil and JwtUserDetailService does not belong to it. So you have to use @MockBean to mock them.
  • Related