-
Notifications
You must be signed in to change notification settings - Fork 1
통합테스트 ‐ Spring Security
스프링 시큐리티 테스트
Controller
, Service
, Repository
레이어에 대한 단위테스트를 진행하고 스프링 시큐리티에 대한 단위테스트또한 하고싶어졌다.
처음엔 스프링 시큐리티의 필터 하나하나 단위테스트 해보려 했다. 하지만 필터에 주입해줘야할 객체들이 많고 복잡해(특히 AuthenticationManager) 통합테스트로 진행하기로 결정했다.
처음엔 @SpringBootTest
로 통합 테스트로 진행했다.
하지만 이렇게 통합 테스트를 진행하니 쓸데없는 Controller
, Repository
등과 JPA 등이 동작했다.
이경우 스프링 시큐리티에 대한 독립적인 테스트가 아니라고 생각해 스프링 시큐리티만 통합테스트 하는 방법을 찾아봤다.
hibernate, SQL 로그가 마구 찍히는것을 볼 수 있다.
스프링 시큐리티만 통합테스트할 수 있는 방법에 대해 생각해보다, @SpringBootTest
의 classes
필드를 사용해 SecurityConfig
만 사용하면 테스트가 가능할것 같아 시도해봤다. 그외, JwtUtil
와 같은 유틸 클래스도 추가해 실행해줬다.
하지만 실행하니 다음과 같은 오류와 함께 실행되지 않았다.
Description:
Parameter 0 of method setFilterChains in org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration required a bean named 'A Bean named mvcHandlerMappingIntrospector of type org.springframework.web.servlet.handler.HandlerMappingIntrospector is required to use MvcRequestMatcher. Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext.' that could not be found.
Action:
Consider defining a bean named 'A Bean named mvcHandlerMappingIntrospector of type org.springframework.web.servlet.handler.HandlerMappingIntrospector is required to use MvcRequestMatcher. Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext.' in your configuration.
WebSecurityConfiguration
을 위해선 HandlerMappingIntrospector
이 필요하지만, 이게 없어서 생기는 문제고, Spring Security 와 Sprinv MVC가 같은 컨텍스트에 있어야 한다는 메시지가 나왔다.
Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext.' that could not be found.
혹시나 해서 @EnableWebMvc
를 사용해줬더니 문제없이 실행할 수 있었다.
@EnableWebMvc
@SpringBootTest(classes = {SecurityConfig.class, JwtUtil.class, ObjectMapper.class})
@AutoConfigureMockMvc
public class LoginFilterTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserDetailsService userDetailsService;
@Test
@WithMockUser
public void 로그인테스트() throws Exception {
BCryptPasswordEncoder bpe = new BCryptPasswordEncoder();
User u = User.builder()
.username("user")
.email("user")
.password(bpe.encode("password"))
.role("ROLE_USER")
.build();
UserDetails userDetails = new CustomUserDetails(u);
when(userDetailsService.loadUserByUsername("user"))
.thenReturn(userDetails);
JSONObject requestBody = new JSONObject();
requestBody.put("username", "user");
requestBody.put("password", "password");
ResultActions perform = mockMvc.perform(MockMvcRequestBuilders.post("/api/login")
.contentType(MediaType.APPLICATION_JSON)
.content(requestBody.toString()));
perform.andExpect(status().isOk());
}
}
내가 테스트하고자 했던 필터는 UsernamePasswordAuthenticationFilter
였고, 이 필터의 경우 UserDetailsService
, UserDetails
를 사용했다.
하지만 UserDetailsService
의 경우 Repository
를 통해 DB에서 유저 데이터를 가져와야 하는 서비스다.
이경우 필터에 대한 테스트 독립성이 깨지기 때문에 UserDetailsService
를 MockBean
으로 만들어 주입했다.
덕분에 DB에 다녀올 필요 없이 시큐리티 필터 레벨에서 테스트가 가능했다.