Spring Security 기반의 테스트 코드 작성하기
목표
- HelloControllerTest (페이지 접근)
- PostsAPIControllerTest (API 호출 기능)
- Spring Security로 인한 접근 권한에 연관된 테스트는 Security 옵션이 필요
- 따라서 작성한 전체 테스트가 정상적으로 수행
- 기존에 SpringSecurity 설정이 되어 있지 않은 상태에서 작성된 테스트 코드는 Security 적용 시 오류가 발생할 수 있다.
API 호출 및 페이지 접근 Controller 테스트 오류 해결하기
이슈 1. CustomOAuth2UserService를 찾을 수 없습니다.
- CustomOAuth2UserService
- 소셜로그인 관련 설정값에 따른 실행
- 기본적으로 application.properties 파일을 src/main, src/test 모두 읽어서 사용한다.
- src/test/resources에 application.properties가 없을 경우 src/main/resources에 있는 application.properties를 자동으로 읽는다.
- src/main/resources/application-oauth.properties는 읽지 못한다.
- 즉, application-oauth.properties가 test쪽에 파일이 없기때문에 발생하는 오류이다.
- 해결책은 application.properties에 오류만 안나도록 설정 값을 임의로 넣는다.
- 소셜로그인 관련 설정값에 따른 실행
- 설정 파일은 application.yml로 작성하였다.
# server setting
server:
port: 8085
# jpa setting
spring:
profiles:
include: oauth # oauth 관련 properties 추가
session:
store-type: jdbc
h2:
console:
enabled: true
jpa:
show_sql: true
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL5InnoDBDialect
# Test 용 security 설정
security:
oauth2:
client:
registration:
google:
client-id: test
client-secret: test
scope:
- profile
- email
이슈 2. 302 Status Code
PostsAPIController 테스트
- Spring Security 설정으로 인한 인증되지 않은 사용자의 요청으로 인해 리다이렉션 응답(302 status code)을 주게 된다.
- 이를 해결하기 위해서는 테스트 메서드에 임의로 인증된 사용자를 추가하여 API만 테스트 할 수 있도록 수정해야 한다.
SpringSecurity Test를 위한 spring-security-test 의존성 추가
dependencies {
// ...
testCompile('org.springframework.security:spring-security-test')
}
- PostsAPIController의 테스트 메서드에 임의 사용자 인증 설정 추가
- @WithMockUser(roles="USER")
- 인증된 임의 사용자를 만들어 사용
- roles 설정으로 권한 추가가 가능
- 해당 어노테이션으로 인하여 ROLE_USER 권한을 가진 사용자가 API를 요청하는 것과 동일한 효과를 갖는다.
- @WithMockUser가 MockMvc에만 작동하기 때문에 MockMvc에서 테스트 할 수 있도록 @Before, mvc.perform 기능을 추가
- @Before
- 매번 테스트가 시작되기 전에 MockMvc 인스턴스를 생성
- mvc.perform
- 생성된 MockMvc를 통해 API를 테스트한다.
- 본문(Body) 영역은 문자열로 표현하기 위해서 ObjectMapper를 통해 문자열 JSON으로 변환
- @WithMockUser(roles="USER")
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class PostsAPIControllerTest {
// ...
@Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
@Before
public void setUp() {
mockMvc = MockMvcBuilders
.webAppContextSetup(webApplicationContext)
.apply(springSecurity())
.build();
}
@Test
@WithMockUser(roles="USER")
public void testSave() throws Exception {
// ...
// when
mockMvc.perform(post(url)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(new ObjectMapper().writeValueAsString(requestDto)))
.andDo(print())
.andExpect(status().isOk());
// ...
}
@Test
@WithMockUser(roles="USER")
public void testUpdate() throws Exception {
// ...
mockMvc.perform(put(url)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(new ObjectMapper().writeValueAsString(requestDto)))
.andDo(print())
.andExpect(status().isOk());
// ...
}
}
이슈 3. @WebMvcTest에서 CustomOAuth2UserService을 찾을 수 없는 문제
- HelloControllerTest의 경우 위에서 처리한 방법과는 다르다.
- 위 이슈를 함으로써 SpringSecurity 설정은 작동했지만 @SpringBootTest와 다르게 @MockMvcTest는 CustomOAuth2UserService를 스캔하지 않는다.
- @WebMvcTest는 WebSecurityConfigurerAdapter, WebMvcConfigurer를 비롯한 @ControllerAdvice, @Controller를 읽는다.
- 즉, @Repository, @Service, @Component는 스캔 대상이 아니다.
- SecurityConfig는 읽었지만 SecurityConfig를 생성하기 위해 필요한 CustomOAuth2UserService는 읽을 수가 없어 오류가 발생했던 것이다.
- 위 문제를 해결하기 위해서는 스캔 대상에서 SecurityConfig를 제거해야 한다.
@RunWith(SpringRunner.class)
@WebMvcTest(
controllers = HelloController.class,
excludeFilters = {
@ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
classes = SecurityConfig.class
)
}
)
public class HelloControllerTest {
@Test
@WithMockUser(roles="USER")
public void return_hello() throws Exception {
}
@Test
@WithMockUser(roles="USER")
public void return_helloDto() throws Exception {
}
}
- 원인 분석
- @EnableJpaAuditing을 사용하기 위해서는 최소 하나의 @Entity 클래스가 필요하다.
- @WebMvcTest의 경우 @Entity 클래스가 없다.
- @EnableJpaAuditing와 @SpringBootApplication 설정을 함께 사용하였기 때문에 @WebMvcTest에서 둘다 읽게 된다.
- 이 문제를 해결하기 위해서는 @EnableJpaAuditing을 사용하기 위한 JpaConfig 설정 클래스를 새로 작성한다.
// @EnableJpaAuditing 제거
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
- @EnableJpaAuditing 설정을 사용하는 JpaConfig 클래스
- JPA Auditing 활성화
@Configuration
@EnableJpaAuditing
public class JpaConfig {}
- 작성한 패키지 구조 및 파일 확인
- 테스트 코드 패키지 및 파일 확인
정리하기
- 스프링 부트 통합 개발환경 설정
- 테스트, JPA 데이터 처리
- Mustache를 활용한 화면 구성
- Security와 OAuth2로 인증, 권한을 적용하여 게시판 작성
'Spring > SpringBoot' 카테고리의 다른 글
[SpringSecurity] Authentication Process(인증 프로세스) (0) | 2020.07.04 |
---|---|
[JWT] JWT(Json Web Token) (0) | 2020.07.04 |
[SpringBoot] OAuth2 Naver Login (2) | 2020.06.01 |
[SpringBoot] 세션 저장소로 DB이용하기 (0) | 2020.06.01 |
[SpringBoot] 어노테이션 기반 세션 처리 ArgumentResolver (0) | 2020.05.31 |