본문 바로가기
개발/Spring

[Spring] SecurityContext에 사용자 정보 넣어 테스트하기

by solchan98 2022. 2. 10.

Security Context에 우리가 원하는 사용자의 정보를 넣어 테스트가 정상적으로 동작할 수 있도록 해보자.

테스트 케이스를 작성하다 보면 사용자 정보 변경, 로그인 된 사용자만 요청가능한 API를 테스트하는 경우가 있다.
그러기 위해서는 Secutiry Context에 사용자 정보가 들어있어야 한다.

기본적으로 Spring Secutity를 사용하여 로그인 서비스가 구성되어있다는 가정하에 진행한다.

WithSecurityContextFactory

WithSecurityContextFactory는 SecutiryContext를 생성하여 테스트 시 사용할 수 있도록 해주는 인터페이스이다.
우리는 이 인터페이스의 구현체를 작성하여 사용할 것 이다.

createSecutiryContext 메서드의 파라미터를 보면 어노테이션을 받는 것을 알 수 있다.
우리는 이 어노테이션을 작성하여 Context가 필요한 테스트 케이스 메서드에서 사용할 것 이다.

메서드의 진행과정은 다음과 같다.

  1. AccountTemplate를 통해 Account객체를 생성한다.
  2. 생성한 Account객체를 통해 UserAccount를 생성한다.
    1. UserAccount는 UserDetails를 구현한 User를 상속받아 직접 작성한 클래스입니다.
  3. 생성한 UserAccount객체를 통해 UsernamePasswordAuthenticationToken을 생성한다.
  4. SecutiryContextHolder를 통해 비어있는 Context를 생성한다.
  5. 생성한 Context에 3번에서 생성한 토큰을 저장한다.
  6. 토큰이 저장된 Context를 반환한다.
public class WithUserDetailsSecurityContextFactory implements WithSecurityContextFactory<TestAccount> {
	@Override
	public SecurityContext createSecurityContext(TestAccount annotation) {
		UserDetails userAccount = new UserAccount(AccountTemplate.account);
		UsernamePasswordAuthenticationToken token = new	UsernamePasswordAuthenticationToken(userAccount, "", userAccount.getAuthorities());
		SecurityContext context = SecurityContextHolder.createEmptyContext();
		context.setAuthentication(token);
		return context;
	}
}

Annotation

이제 우리가 생성한 Context를 사용하기 위해 어노테이션을 작성하고 이를 사용할 것이다.

  1. 작동 범위를 Runtime으로 설정한다.
  2. @WithSecurityContext를 통해 우리가 작성한 클래스를 설정한다.

이제 Context가 필요한 테스트 케이스 메서드에 이 어노테이션을 사용해주면 된다.

@Retention(RetentionPolicy.RUNTIME)
@WithSecurityContext(factory = WithUserDetailsSecurityContextFactory.class)
public @interface TestAccount { }

사용 예시

WebMvcTest 하나의 예시를 들어보자.

아래는 SecurityContext에 사용자 정보가 들어있어야만 접근이 가능한 Controller 하나의 메서드 예이다.

메서드 파라미터 부분에 @AuthAccount를 통해 Account를 받는 것을 알 수 있다.
이것은 SecutiryContext에서 사용자 정보를 받아오는 방법이다. 어노테이션으로 로그인한 사용자 정보 받아오기
따라서 SecurityContext에 사용자 정보가 존재해야한다.

@PatchMapping("/password")
public CustomResponseEntity<Message> updatePassword(@AuthAccount Account account, @RequestBody PasswordRequest passwordRequest) {
	accountService.updatePassword(account, passwordRequest);
	return new CustomResponseEntity<>(
		Message.of(HttpStatus.OK, AccountContent.PASSWORD_UPDATE_OK)
	);
}

@TestAccount를 테스트 케이스 메서드에 사용하면 SecurityContext에 우리가 지정한 Account가 들어가있다.
따라서 정상적으로 위 @AuthAccount를 통해 Account객체를 얻을 수 있다.

@Test
@TestAccount
void updatePasswordFailByNotEqual() {
	...
}

전체코드