Junit5:WebMvcTest returns 404。可能是因为我没有模拟底层方法?

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

序言:我正在学习 Java、Spring 引导和总体......使用 Java/Spring 引导的 TDD。

使用的版本:

这是我的控制器:

@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));
    }
}

相关autowiredJwtAuthentication:

@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);
        }
    }
}

我为 JwtAuthentication 本身编写的测试没有问题,现在我需要测试控制器。

这是我的测试:

@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());
    }
}

但我得到的是 404 而不是 200。

我读到原因是缺少模拟底层方法。实际上,控制器上的这些测试不会启动整个配置(等等)。在 S.O..

上找不到答案 atm

所以,我认为解决方案需要在测试中添加一个“简单的”when

@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());
    }

404 是因为没有控制器来处理请求。

因为您在@WebMvcTest中指定了错误的控制器,导致您要测试的控制器没有包含在spring容器中。所以更改为以下内容应该可以解决问题:

@WebMvcTest(LoginController.class)
class LoginControllerTest {

    @MockBean
    private JwtAuthentication jwtAuthentication;

    @MockBean
    private JwtTokenUtil jwtTokenUtil;

    @MockBean
    private JwtUserDetailService jwtUserDetailService;

}

还要注意以下几点:

  • 我删除了 @ExtendWith(SpringExtension.class),因为 @WebMvcTest 已经包含了它
  • @WebMvcTest 只会启用与 web 层相关的 bean(参见 this),而 JwtTokenUtilJwtUserDetailService 不属于它。所以你必须使用 @MockBean 来模拟它们。