Spring

[SpringBoot] 로그인된 사용자가 접근할 수 있는 기능 Test 작성하는 방법

꾸준함. 2022. 4. 2. 01:04

개요

로그인된 사용자를 대상으로 하는 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 Security Test

Spring Security를 사용하면 권한을 이용한 테스트를 할 때가 있다. @WithMockUser 어노테이션을 사용하고 싶은데 커스텀 Authentication 객체를 사용할 때, 이를 그대로 사용할 수는 없다. 이걸 어떻게 쓰려

smjeon.dev

 

반응형