본문 바로가기
Clone Coding/스프링 부트와 AWS

[Spring Security] 기존 테스트에 시큐리티 적용하기

by 연로그 2021. 1. 22.
반응형

테스트 코드를 작성했을 때는 API를 바로 호출해서 만들었다.

하지만 시큐리티 옵션이 활성화되면서 인증된 사람만 API를 호출할 수 있게 되어 테슽트 코드마다 인증한 사용자가 호출한 것처럼 작동하도록 수정해야 한다.

 

일단 전체 테스트를 한 번 돌려보자

문제1

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'securityConfig' defined in file [..\SecurityConfig.class] :Unsatisfied dependency expressed through constructor parameter 0;  org.springframework.beans.factory.NoSuchBeanDefinitionException:

No qualifying bean of type com.spaws.book.springboot.config.auth.CustomOAuth2UserService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}

 

여러 오류 메시지 중 이런 메시지가 눈에 띌 것이다.

CustomOAuth2UserService를 생성하는데 필요한 소셜 로그인 관련 설정 값들이 없기 때문에 발생한다.

 

그렇다면 여기서 의문점이 생긴다.

 

application-oauth.properties의 값들을 읽어오지 못하는걸까?

-> test에 application.properties가 없으면 main의 설정을 그대로 가져온다. 

   여기서 문제는 자동으로 가져오는건 application.properties뿐이고 applicaiton-oauth.properties는 제외된다.

 

어떻게 해결해야 할까?

-> 테스트를 위한 application.properties를 만들자.

src/test/resources에 application.properties를 생성한다.

spring.jpa.show_sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.h2.console.enabled=true
spring.mvc.hiddenmethod.filter.enabled=true
spring.session.store-type=jdbc

#Test OAuth
spring.security.oauth2.client.registration.google.client-id=test
spring.security.oauth2.client.registration.google.client-secret=test
spring.security.oauth2.client.registration.google.scope=profile, email

 

다시 jUnit을 실행시켜 보면 이번엔 302 FOUND Error을 확인할 수 있을 것이다.

임시로 만든 client-id=test, client-secret=test라는 인증되지 않은 사용자의 요청이기 때문이다.

이럴 때는 임의로 인증된 사용자를 추가해 API만 테스트할 수 있다.

 

build.gradle에 다음 코드를 추가한다.

testCompile("org.springframework.security:spring-security-test")

 

PostsApiControllerTest의 두 메소드에 임의 사용자 인증을 추가한다.

@Test
@WithMockUser(roles="USER")
public void Posts_등록() throws Exception {
	...
}
	
@Test
@WithMockUser(roles="USER")
public void Posts_업데이트() throws Exception {
	...	
}

코드 설명 ▼

더보기

@WithMockUser(roles="USER")

- 인증된 모의 사용자를 만들어서 사용

- roles에 권한 추가 가능

- MockMvc에서만 작동

 

@SpringbootTest에서 MockMvc를 사용하는 방법

PostsApiControllerTest 수정

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class PostsApiControllerTest {
	...
	@Autowired
	private WebApplicationContext context;
	
	private MockMvc mvc;
	
	@Before
	public void setup() {
		mvc = MockMvcBuilders
				.webAppContextSetup(context)
				.apply(springSecurity())
				.build();
	}
	...
    
	@Test
	@WithMockUser(roles="USER")
	public void Posts_등록() throws Exception {
		//given
		...
		
		//when
		mvc.perform(post(url)
				.contentType(MediaType.APPLICATION_JSON_UTF8)
				.content(new ObjectMapper().writeValueAsString(requestDto)))
				.andExpect(status().isOk());
                
		//then
		List<Posts> all = postsRepository.findAll();
		assertThat(all.get(0).getTitle()).isEqualTo(title);
		assertThat(all.get(0).getContent()).isEqualTo(content);
		
	}
	
	@Test
	@WithMockUser(roles="USER")
	public void Posts_업데이트() throws Exception {
		//given
		...
		
		//when
		mvc.perform(post(url)
				.contentType(MediaType.APPLICATION_JSON_UTF8)
				.content(new ObjectMapper().writeValueAsString(requestDto)))
				.andExpect(status().isOk());
		
		//then
		List<Posts> all = postsRepository.findAll();
		assertThat(all.get(0).getTitle()).isEqualTo(expectedTitle);
		assertThat(all.get(0).getContent()).isEqualTo(expectedContent);
		
	}
}

 

문제2

java.lang.IllegalStateException: Failed to load ApplicationContext

...

No qualifying bean of type com.spaws.book.springboot.config.auth.CustomOAuth2UserService available
: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}

 

1번과 똑같은 문제다?

언뜻 보면 문제 1번과 같은 오류인 것 같지만 조금 다른 점이 있다.

2번에서는 @WebMvcTest를 사용한다. 이 어노테이션은 CustomOAuth2UserService를 스캔하지 않는다.

 

@WebMvcTest

- 읽는 것: WebSecurityConfigurerAdapter, WebMvcConfigurer,  @ControllerAdvice, @Controller

- 읽지 않는 것: @Repository, @Service, @Component, ...

 

결국 SecurityConfig는 읽었지만, SecurityConfig를 생성하기 위해 필요한 CustomOAuth2UserService를 읽을 수 없어서 에러가 발생한 것이다.

 

어떻게 해결해야 할까?

HelloControllerTest

@RunWith(SpringRunner.class)
@WebMvcTest(controllers = HelloController.class,
	excludeFilters = {
		@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = SecurityConfig.class)
	})
public class HelloControllerTest {
	@Autowired
	private MockMvc mvc;
	
	@WithMockUser(roles="USER")
	@Test
	public void hello_리턴() throws Exception {
		...
	}
	
	@WithMockUser(roles="USER")
	@Test
	public void helloDto_리턴() throws Exception {
		...
	}
}

일단 스캔 대상에서 SecurityConfig를 제외하고,

@WithMockUser를 통해 가짜 인증 사용자를 생성한다.

 

재실행하면 이번에는 java.lang.illegalStateException:Failed to load ApplicationContext가 뜰 것이다.

Application.java를 보면 @EnableJpaAuditing 어노테이션이 있는데, 이를 사용하기 위해선 최소 하나의 @Entity 클래스가 필요하다.

하지만 위 코드에서는 @WebMvcTest이다보니 Entity가 없다.

그래서 Application과 @EnableJpaAuditing을 분리해보겠다.

 

일단 Application에서 @EnableJapAuditing 어노테이션을 제거한다.

config폴더에 JpaConfig 클래스를 생성한다.

@Configuration
@EnableJpaAuditing
public class JpaConfig { }

 

다시 테스트를 수행하면 무사히 수행되는 것을 확인할 수 있다.


해당 게시글은 [ 스프링 부트와 AWS로 혼자 구현하는 웹 서비스 / 이동욱 ] 책을 따라한 것을 정리하기 위한 게시글입니다. 요약, 생략한 부분이 많으니 보다 자세한 설명은 책 구매를 권장합니다.

반응형