개요
로그인된 사용자를 대상으로 하는 Controller 테스트를 작성할 때 저는 @BeforeEach 어노테이션을 통해 form 로그인을 먼저 진행하고 @WithUserDetails의 setUpBefore를 TEST_EXECUTION으로 설정하여 @BeforeEach 어노테이션이 호출된 뒤 테스트를 진행하도록 구현했습니다.
하지만, 테스트는 제가 원했던 순서와 달리 @WithUserDetails 어노테이션이 붙어있는 테스트가 @BeforeEach가 먼저 실행되어 에러가 발생했습니다.
찾아본 결과 이는 JUnit5 Spring Security 버그이며 현재 2022.04.02 기준으로도 아직 해결이 되지 않은 상태입니다.
따라서, 저는 커스텀 어노테이션과 @WithSecurityContext 어노테이션 그리고 WithSecurityContextFactory 구현체를 통해 위 문제를 해결하였고 해결법을 간단히 공유하고자 합니다.
예상과 다르게 작동된 코드
1. 로그인된 사용자만 접근할 수 있는 기능 테스트하는 방법
인증된 사용자만 접근할 수 있는 기능을 테스트하기 위해서는 실제 DB에 저장되어 있는 정보에 대응하는 인증된 Authentication이 필요합니다.
따라서, @WithMockUser 어노테이션으로는 해결할 수 없고 커스텀 어노테이션을 아래와 같이 만들어줘야 합니다.
1.1 커스텀 어노테이션
* Runtime까지 유지돼야 하므로 RetentionPolicy를 런타임으로 부여하고 Authentication을 부여할 WithSecurityContextFactory 구현체를 @WithSecurityContext 어노테이션에 지정해주면 됩니다.
* String value() 필드를 넣어준 이유는 아이디를 넘겨주기 위함입니다.
1.2 WithSecurityContextFactory 구현체
* 넘겨받은 아이디를 기반으로 계정을 생성해주고
* Authentication 생성 후 SecurityContext를 넣어준 뒤 반환해주면 됩니다.
* 해당 구현체는 Bean을 주입받을 수 있기 때문에 @Autowired 어노테이션 사용 가능합니다.
1.3 실제 테스트 적용
1.1과 1.2 과정을 거치면 실제 테스트에 적용할 수 있습니다.
적용 방법은 아래와 같습니다.
* WithSecurityContextFactory 구현체에서 매번 새로운 계정을 생성해주는 것이기 때문에 @AfterEach 어노테이션을 통해 테스트가 끝날 때마다 계정을 삭제해줘야 합니다. 삭제를 해주지 않을 경우 아이디가 중복되어 에러가 발생합니다.
2. 비고
2.1 @MockUser
- Spring Security의 User 객체를 사용하고 기본 Authentication 객체를 user 객체로 채운 뒤 SecurityContext 내 Authentication Setting을 진행
- username "user", password "password"인 "ROLE_USER" 권한을 가진 채 테스트를 진행
- 1번에서 @MockUser를 사용하지 못한 이유는 DB에 존재하는 계정 즉, 인증된 Authentication이 아니기 때문
* mocking하기 때문에 "user"라는 유저가 실제로 없어도 됨 (즉, 인증된 authentication이 아니어도 됨)
* UsernamePasswordAuthenticationToken 타입의 Authentication 객체가 SecurityContext 내 생성
* Authentication 객체 내의 principal은 Spring Security의 User 객체
2.2 @WithAnonymousUser
- 특정 유저가 아닌 익명의 사용자로 테스트를 돌리는 어노테이션
- 해당 어노테이션은 테스트의 일부분만 anonymous로 진행하고자 할 때 유용
- 아래와 같이 나머지 부분은 특정 유저로 진행
참고
인프런 강의 - 스프링과 JPA 기반 웹 애플리케이션 개발 (백기선 강사님)
https://smjeon.dev/etc/with-mock-user/#%EC%B0%B8%EA%B3%A0%EC%9E%90%EB%A3%8C
'Spring' 카테고리의 다른 글
[SpringBoot] 구글 SMTP 통해 메일 보내기 (2) | 2022.04.10 |
---|---|
[SpringBoot] BcryptPasswordEncoder (0) | 2022.04.03 |
[SpringBoot] OpenSessionInView (0) | 2022.03.29 |
[SpringBoot] AOP 간단 정리 (0) | 2022.01.12 |
[SpringBoot] @Aspect 어노테이션 (0) | 2021.12.23 |