簡體   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