我正在开发一个带有spring Boot 和spring security的rest api。
代码如下所示:
@RestController
@RequestMapping(path = "/api")
@PreAuthorize("isAuthenticated()")
public class RestController {
@GetMapping(path = "/get", produces = "application/json")
public ResponseEntity<InDto> get(
@AuthenticationPrincipal final CustomUser user) {
// ...
return ResponseEntity.ok(outDto);
}
}
public class CustomUser {
// does not inherit from UserDetails
}
public class CustomAuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(
@NonNull final HttpServletRequest request,
@NonNull final HttpServletResponse response,
@NonNull final FilterChain filterChain)
throws ServletException, IOException {
if (/* condition */) {
// ...
final CustomUser user = new CustomUser(/* parameters */);
final Authentication authentication =
new PreAuthenticatedAuthenticationToken(user, "", new ArrayList<>());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
}
我希望在没有安全特性的情况下对RestController类进行单元测试,但我不知道如何在测试期间注入特定的CustomUser对象。
我尝试在每次测试前手动添加一个用户到安全上下文(见下文),但是在测试期间注入控制器的用户不是模拟的。
@WebMvcTest(RestController.class)
@AutoConfigureMockMvc(addFilters = false)
class RestControllerTest {
@Autowired private MockMvc mockMvc;
private CustomerUser userMock;
@BeforeEach
public void skipSecurityFilter() {
userMock = Mockito.mock(CustomUser.class);
SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());
final Authentication auth = new PreAuthenticatedAuthenticationToken(userMock, null, List.of());
SecurityContextHolder.getContext().setAuthentication(auth);
}
@Test
void test() {
mockMvc.perform(
MockMvcRequestBuilders.get("/api/get")
.contentType(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isOk());
}
}
如何将特定的userMock注入到控制器中进行测试?
编辑以使用@WithMockCustomUser进行测试
如文档www.example.com中所建议https://docs.spring.io/spring-security/reference/servlet/test/method.html#test-method-withsecuritycontext我已将测试更新为:
@Retention(RetentionPolicy.RUNTIME)
@WithSecurityContext(factory = WithMockCustomUserSecurityContextFactory.class)
public @interface WithMockCustomUser {
}
@Service
public class WithMockCustomUserSecurityContextFactory
implements WithSecurityContextFactory<WithMockCustomUser> {
@Override
public SecurityContext createSecurityContext(final WithMockCustomUser customUser) {
final SecurityContext context = SecurityContextHolder.createEmptyContext();
final Authentication auth =
new PreAuthenticatedAuthenticationToken(Mockito.mock(IUser.class), null, List.of());
context.setAuthentication(auth);
return context;
}
}
@WebMvcTest(RestController.class)
@AutoConfigureMockMvc(addFilters = false)
class RestControllerTest {
@Autowired private MockMvc mockMvc;
private CustomerUser userMock;
@BeforeEach
public void skipSecurityFilter() {
userMock = Mockito.mock(CustomUser.class);
}
@Test
@WithMockCustomUser
void test() {
mockMvc.perform(
MockMvcRequestBuilders.get("/api/get")
.contentType(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isOk());
}
}
但控制器中的用户对象仍然不是mock(在工厂中创建)
1条答案
按热度按时间hm2xizp91#
我重写了测试以初始化测试中的安全上下文
而且很管用。
不确定为什么它不能与@BeforeEach一起工作。