繁体   English   中英

如何使用 Spring Security 和 Mockito 模拟 AuthenticationManager 身份验证方法

[英]How to mock AuthenticationManager authenticate method using Spring Security and Mockito

我正在尝试使用 Mockito 来测试用户是否点击登录 api 时,它会以 JWT 令牌响应。 但是,我不断收到来自 Spring 安全性中的 authenticationManager.authenticate() 方法的Bad Credentials错误。 我现在正在尝试模拟这种方法,但我不断收到各种不同的错误,并且不确定我的方法是否正确。 这是我最新的实现,现在失败,因为You cannot use argument matchers outside of verification or stubbing因为它不喜欢我使用模拟的方式。

@WebMvcTest(value = UserCommandController.class, includeFilters = {
    // to include JwtUtil in spring context
    @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = JwtUtil.class)})
class UserCommandControllerTest {

Logger logger = LoggerFactory.getLogger(UserCommandControllerTest.class);

@MockBean
private UserCommandService userCommandService;

@Autowired
private MockMvc mockMvc;

@MockBean
private UserDetailsServiceImpl userDetailsServiceImpl;

@MockBean
private JwtUtil jwtUtil;

@Autowired
private JwtRequestFilter jwtRequestFilter;

@Autowired
AuthenticationManager authenticationManager;

private static UserDetails dummy;
private static String jwtToken;

@BeforeEach
public void setUp() {
    dummy = new User("user@email.com", "123456", new ArrayList<>());
    jwtToken = jwtUtil.generateToken(dummy);
}


@Test
void testLoginReturnsJwt() throws Exception {

    AuthenticationRequest authenticationRequest = new AuthenticationRequest("user@email.com", "123456");
    AuthenticationResponse authenticationResponse = new AuthenticationResponse("anyjwt");

    String jsonRequest = asJsonString(authenticationRequest);
    String jsonResponse = asJsonString(authenticationResponse);

    RequestBuilder request = MockMvcRequestBuilders
            .post("/api/adverts/user/login")
            .content(jsonRequest)
            .contentType(MediaType.APPLICATION_JSON_VALUE)
            .accept(MediaType.APPLICATION_JSON);

    Authentication authentication = mock(Authentication.class);
    authentication.setAuthenticated(true);
    when(authentication.isAuthenticated()).thenReturn(true);

    when(authenticationManager.authenticate(any())).thenReturn(authentication); // Failing here

    when(jwtUtil.generateToken(dummy)).thenReturn("124");
    when(userDetailsServiceImpl.loadUserByUsername(eq("user@email.com"))).thenReturn(dummy);

    MvcResult mvcResult = mockMvc.perform(request)
            .andExpect(status().is2xxSuccessful())
            .andExpect(content().json(jsonResponse, true))
            .andExpect(jsonPath("$.jwt").value(isNotNull()))
            .andReturn();
}

这是我的 controller:

@PostMapping(value = "/login", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> loginUser(@Valid @RequestBody AuthenticationRequest authenticationRequest) throws Exception {

    try {
        Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword()));
    } catch (BadCredentialsException e) {
        throw new Exception("incorrect username or password", e);
    }

    UserDetails userDetails = userDetailsService.loadUserByUsername(authenticationRequest.getUsername());

    String jwt = jwtTokenUtil.generateToken(userDetails);

    return ResponseEntity.ok(new AuthenticationResponse(jwt));
}

谢谢你。

PS:这是我的仓库,其中设置了 AuthenticationManager: https://github.com/francislainy/adverts-backend/blob/dev_jwt/src/main/java/com/example/adverts/MyWebSecurity.java#L30

您需要AuthenticationManager是一个模拟,而在您的代码中它不是( @Autowired注入一个“真实”实例)。 您可以使用MockBean注解模拟 bean:

@MockBean
AuthenticationManager authenticationManager;

现在你可以随心所欲地模拟authenticationManager了。

您的代码的另一个问题是您在断言中滥用ArgumentMatchers.isNotNull()匹配器导致异常:

org.mockito.exceptions.misusing.InvalidUseOfMatchersException: 
Misplaced or misused argument matcher detected here:

-> at com.example.adverts.controller.user.UserCommandControllerTest.testLoginReturnsJwt(UserCommandControllerTest.java:356)

You cannot use argument matchers outside of verification or stubbing.
Examples of correct usage of argument matchers:
    when(mock.get(anyInt())).thenReturn(null);
    doThrow(new RuntimeException()).when(mock).someVoidMethod(any());
    verify(mock).someMethod(contains("foo"))

This message may appear after an NullPointerException if the last matcher is returning an object 
like any() but the stubbed method signature expect a primitive argument, in this case,
use primitive alternatives.
    when(mock.get(any())); // bad use, will raise NPE
    when(mock.get(anyInt())); // correct usage use

Also, this error might show up because you use argument matchers with methods that cannot be mocked.
Following methods *cannot* be stubbed/verified: final/private/equals()/hashCode().
Mocking methods declared on non-public parent classes is not supported.

ArgumentMatchers提供了在方法调用存根和验证中使用的匹配器。 您应该改用org.hamcrest.CoreMatchers#notNullValue()

通过这些修复,您的测试全部通过绿色。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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