Issue
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());
}
Solution
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) whichJwtTokenUtil
andJwtUserDetailService
does not belong to it. So you have to use@MockBean
to mock them.
Answered By - Ken Chan
Answer Checked By - David Marino (JavaFixing Volunteer)