[스프링부트와 AWS로 혼자 구현하는 웹 서비스] 기존 테스트에 Spring Security 적용

세이라·2023년 8월 5일
0

스터디를 통해 스프링부트와 AWS로 혼자 구현하는 웹 서비스(저자 이동욱) 서적을 공부하는 중입니다.

공부/실습한 내용을 정리한 포스팅입니다.
책에 모르는 부분이 있으면 구글링하거나 챗gpt에 물어봐서 보충하였습니다.
(아직 초보라 모르는 부분이 많아 이것저것 다 적었습니다.)

참고한 사이트 출처는 포스팅 맨 하단에 적었습니다.


기존의 테스트에서는 바로 API 호출이 가능했지만, Spring Security 적용 후엔 인증된 사용자만이 API 호출할 수 있음.
테스트 코드마다 인증한 사용자가 호출한 것처럼 작동하도록 수정.


1. 전체 테스트 수행

gradle > Tasks > verification > test 더블 클릭


2. CustomOAuth2UserSerivce 찾을 수 없음 문제 해결

hello가_리턴된다 예외 메세지

No qualifying bean of type 'com.webservice.springboot.springboot_board.config.auth.CustomOAuth2UserService'

  • CustomOAuth2UserService 생성 시 필요한 소셜 로그인 관련 설정값들이 없기 때문에 발생.
  • application-oauth.properties에 설정값 추가했는데도 왜 설정이 없다고 할까?
    : src/main 환경과 src/test 환경의 차이. test에 application.properties가 없으면 main의 설정을 그대로 가져오는데, 이때 자동으로 가져오는 옵션의 범위는 application.properties에 한함.(즉, application-oauth.properties는 test에 파일이 없다고 가져오는 파일은 아님.)

→ 테스트 환경을 위한 application.properties 생성. 실제로 구글 연동까지 진행은 안하므로 가짜 설정값 등록.


3. 302 Status Code 문제 해결

expected:<[200 OK]> but was:<[302 FOUND]>
Expected :200 OK
Actual :302 FOUND

  • 302(리다이렉션 응답) Status Code가 와서 실패.
  • 이는 Spring Security 설정 때문에 인증되지 않은 사용자의 요청은 이동시키기 때문.
  • 임의로 인증된 사용자를 추가하여 API만 테스트해볼 수 있게 함.
  1. Spring Security Test를 위한 여러 도구를 지원하는 spring-security-test build.gradle 의존성에 추가.
    testImplementation('org.springframework.security:spring-security-test')
  1. Posts_등록된다(),Posts_수정된다()@WithMockUser(roles="USER") 추가
  • 인증된 모의(가짜) 사용자 만들어서 사용.
  • roles에 권한 추가.
  • 이 annotation을 통해, ROLE_USER 권한을 가진 사용자가 API를 요청하는 것과 동일한 효과를 지니게 됨.
  1. @WithMockUser가 MockMvc에서만 작동하기 때문에, @SprinBootTest에서 MockMvc 사용하도록 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{
    	.
        .
        .
		//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 {
  		.
        . 
       	.
        //when
       mvc.perform(put(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);
    }
  • @Before : 매번 테스트 시작 전에 MockMvc 인스턴스 생성
  • ObjectMapper : 본문(Body) 영역을 문자열로 표현하기 위해 ObjectMapper를 통해 문자열 JSON으로 변환.

부가 설명

ObjectMapper

  • Java 객체와 JSON 데이터 간의 변환을 담당하는 Jackson 라이브러리의 핵심 클래스. Jackson은 Java 객체를 JSON으로 직렬화하거나, JSON 데이터를 Java 객체로 역직렬화하는 기능 제공하여 데이터를 간편하게 변환할 수 있도록 도와줌.
  • WebApplicationContext : Spring 웹 애플리케이션에서 사용되는 ApplicationContext의 하위 인터페이스. 웹 환경에서 사용되는 Bean들을 관리하고, 웹 애플리케이션에서 필요한 서비스와 기능을 제공.(웹 환경 지원, DispatcherServlet 연동, 서블릿 환경 지원 등) 웹 애플리케이션 설정을 관리하고 웹 요청을 처리하는 데 필요한 빈들을 제공하는 중요한 Container.

MockMvc와 WebApplicationContext

  • 가상의 웹 환경 구성을 위해 WebApplicationContext 필요.

4. @WebMvcTest에서 CustomOAuth2UserService를 찾을 수 없음 문제 해결

1에서 봤던 hello가_리턴된다 예외 메세지가 또 나옴.

No qualifying bean of type 'com.webservice.springboot.springboot_board.config.auth.CustomOAuth2UserService'

HelloControllerTest과 다르게 @WebMvcTest 사용. @WebMvcTestCustomOAuth2UserService 스캔을 하지 않음.
(@WebMvcTest는 WebSecurityConfigurerAdapter, WebMvcConfigurer를 비롯한 @Controller,@ControllerAdvice를 읽음. 즉, @Repository, @Service, @Component는 스캔 대상이 아님. 웹 계층을 테스트하기 때문에, 웹 관련 빈들만 로드.)

SecurityConfig는 읽었지만, SecurityConfig를 생성하기 위해 필요한 CustomOAuth2UserService는 읽을 수가 X 그래서 error 발생.
⇒ 스캔 대상에서SecurityConfig 제외

HelloControllerTest에서 아래와 같이 추가

  • 클래스에 어노테이션 추가
@RunWith(SpringRunner.class)
@WebMvcTest(controllers = HelloController.class,
            excludeFilters = {
            @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = SecurityConfig.class)
            })
public class HelloControllerTest {
  • hello가_리턴된다(), helloDto가_리턴된다() 메서드에 아래와 같이 추가
    @Test
    @WithMockUser(roles = "USER")
    public void hello가_리턴된다() throws Exception{
    	.
    	.
    	.
    }

    @Test
    @WithMockUser(roles = "USER")
    public void  helloDto가_리턴된다() throws Exception{
     	.
        .
        .

5. @EnableJpaAuditing로 인한 에러 문제 해결

Error creating bean with name 'jpaAuditingHandler': Cannot resolve reference to bean 'jpaMappingContext' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaMappingContext': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: JPA metamodel must not be empty!

@EnableJpaAuditing 인하여 발생. @EnableJpaAuditing 사용하기 위해선 최소 하나의 @Entity 클래스가 필요한데, @WebMvcTest라 없음. @EnableJpaAuditing@SpringBootApplication이랑 같이 있어 @WebMvcTest에서도 스캔.
@EnableJpaAuditing@SpringBootApplication 분리.

(1) Application 클래스에서 @EnableJpaAuditing 제거
(2) config 패키지에 JpaConfig 생성 후 @Configuration,@EnableJpaAuditing 붙이기.

package com.webservice.springboot.springboot_board.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@Configuration
@EnableJpaAuditing
public class JpaConfig {
}

@WebMvcTest는 일반적인 Configuration 스캔X
※ Spring Boot에서는 @SpringBootApplication을 사용하면 자동으로 @Configuration 어노테이션이 포함된 클래스를 스캔하여 Bean으로 등록.
@SpringBootApplication@Configuration, @EnableAutoConfiguration, 그리고 @ComponentScan 어노테이션을 합친 것과 동일한 역할을 수행.
@WebMvcTest은 웹과 관련된 빈들을 테스트하기 위해 @SpringBootApplication이 지정된 메인 애플리케이션 클래스를 읽어들임.

0개의 댓글