기존 테스트에 시큐리티 적용하기

nn·2022년 2월 6일
0
post-custom-banner

기존 테스트에 시큐리티를 적용하면 시큐리티 옵션이 활성화되어 인증된 사용자만 API를 호출 할 수 있습니다.

그렇기 때문에 인증 권한을 받지 못한 기존 API테스트 코드들을 인증받은 사용자가 호출 한 것처럼 수정을 해야합니다.


프로퍼티 설정 값 등록

src/main 과 src/test 는 서로 다른 환경구성을 가지기 때문에 application-oauth.properties의 설정값을 추가했어도 테스트시에는 application.properties의 설정값까지만 가져오게됩니다.

test에 application.properties가 따로 없으면 mian의 application.properties를 사용합니다.

이 문제를 해결하기 위해 테스트 환경을 위해 test아래에 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.profiles.include=oauth
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

구글 연동은 실제로 하지 않을 것이기때문에 위와 같이 가짜 설정값을 입력했습니다.


인증된 사용자 추가

스프링 시큐리티 설정때문에 인증되지 않은 사용자의 요청은 이동시키기 때문에 임의로 인증된 사용자를 추가하겠습니다.

build.gradle에 spring-security-test를 추가하겠습니다.

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

임의의 사용자 인증을 추가하겠습니다.

PostsApiContollerTest.java

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) // 랜덤포트실행
public class PostsApiControllerTest {

    @LocalServerPort
    private int port;

    @Autowired
    private TestRestTemplate restTemplate; // HTTP를 사용하여 통신하는 범용 라이브러리

    @Autowired
    private PostsRepository postsRepository;

    @Autowired
    private WebApplicationContext context;

    private MockMvc mvc;

    @After
    public void tearDown() throws Exception {
        postsRepository.deleteAll();
    }

    @Before
    public void setup(){ // 테스트가 시작되기 전에 MockMvc 인스턴스생성.
        mvc = MockMvcBuilders
                .webAppContextSetup(context)
                .apply(springSecurity())
                .build();
    }

    @Test
    @WithMockUser(roles="USER")
    public void Posts가_등록된다() throws Exception {
        //given
        String title = "테스트 제목입니다.";
        String content = "테스트 내용입니다.";
        PostsSaveRequestDto requestDto = PostsSaveRequestDto.builder()
                .content(content)
                .title(title)
                .author("작성자")
                .build();

        String url = "http://localhost:"+ port + "/api/v1/posts";

        //when
        // mvc를 이용해서 테스트 문자열로 본문을 표시하기위해 Json으로 변환
        mvc.perform(post(url)
                .contentType(MediaType.APPLICATION_JSON_UTF8)
                .content(new ObjectMapper().writeValueAsString(requestDto)))
                .andExpect(status().isOk());

        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 수정할 게시글 작성
        Posts savePosts = postsRepository.save(Posts.builder()
                .title("수정테스트 제목")
                .content("수정테스트 내용")
                .author("수정테스트 작성자")
                .build());

        Long updateId = savePosts.getId();// 작성한 게시글에서 아이디 가져오기
        String expectTitle = "새 제목"; // 수정할 제목
        String expectContent = "새 내용"; // 수정할 내용

        PostsUpdateRequestDto requestDto = PostsUpdateRequestDto.builder()
                .title(expectTitle)
                .content(expectContent)
                .build();

        String url = "http://localhost:" + port + "/api/v1/posts/" + updateId;

        HttpEntity<PostsUpdateRequestDto> requestEntity = new HttpEntity<>(requestDto);

        //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(expectTitle);
        assertThat(all.get(0).getContent()).isEqualTo(expectContent);
    }

}

@WithMockUser(roles="USER") 는 roles에 권한을 추가해 가짜 사용자를 만들어 사용할 수 있습니다.


@WebMvcTest@ControllerAdvice@Controller를 읽으므로 @Repository, @Service, @Component는 스캔대상이 아니기 때문에 securityConfig 설정을 읽기위한 서비스는 읽을 수 없습니다.

그래서 securiryConfig를 스캔대상에서 제거하고 @WithMockMvc를 이용해서 가짜 사용자를 생성하겠습니다.

HelloControllerTest.java

@WebMvcTest(controllers = HelloController.class,
excludeFilters = {
      @ComponentScan.Filter( type = FilterType.ASSIGNABLE_TYPE, classes = SecurityConfig.class)
        })
public class HelloControllerTest {
        ...
  @WithMockUser(roles="USER")
    @Test
    public void hello를_리턴한다() throws Exception {
    ...
    }
  @WithMockUser(roles="USER")
    @Test
    public void helloDto가_리턴된다() throws Exception {
    ...
    }

@EnableJpaAuditing를 사용하기위해선 하나의 엔티티 클래스가 필요하지만 MockMvcTest를 사용하는 경우 엔티티가 없습니다.

그렇기 때문에 @SpringBootApplication@EnableJpaAuditing를 분리하겠습니다.

Application.java

~~@EnableJpaAuditing ~~삭제
@SpringBootApplication
public class Application {
    public static void main(String[] args){
        SpringApplication.run(Application.class, args);
    }
}

config패키지에 JpaConfig클래스를 새로 만들어 @EnableJpaAuditing를 추가했습니다.

@Configuration
@EnableJpaAuditing  // JPA Auditing 활성화
public class JpaConfig {}

@WebMvcTest는 일반적인 @Configuration은 스캔하지 않습니다.

profile
내가 될 거라고 했잖아
post-custom-banner

0개의 댓글