진짜 개발자
본문 바로가기

FrameWork/Spring Security

Spring Security - Spring MVC Test하기(@WithMockUser, form login 리다이렉션 해결)

728x90

Sprint security MVC Test

Spring MVC를 이용해 웹을 개발하면, 인증 및 허가를 편리하게 다루기 위해 Spring Security를 사용합니다. 또한 어플리케이션을 개발하다보면 필수적으로 테스트코드를 작성하게 됩니다.

하지만, 이때 Security Form Login이 적용되어 있다면, 자동으로 유저를 로그인 페이지로 리다이렉션 시키키 때문에, 우리가 원하는 테스트를 진행하기 어렵습니다.




Spring Security 사용시 MVC Test하기

1. Test Project 생성

완성된 프로젝트 구조입니다. 아래에서 각각의 파일들을 자세히 알려드리겠습니다.


build.gradle

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    implementation 'org.springframework.boot:spring-boot-starter-web'
}

build.gralde 파일의 의존성 설정에 위와 같이 스프링 부트 스타터 security, thymeleaf, web를 추가합니다.



SecurityConfig.class

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .cors().disable()
                .csrf().disable()
                .authorizeRequests()
                    .anyRequest().authenticated()
                .and()
                .formLogin();
    }
}

Spring security 설정 파일 클래스를 만들고 위와같이, form login 설정을 합니다.



TestController.class

@Controller
public class TestController {
    @GetMapping("/test")
    public String getTestPage() {
        return "test";
    }
}

test.html을 반환하는 handler를 포함하는 컨트롤러를 생성합니다.



test.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>test</title>
</head>
<body>
    TEST 페이지입니다.
</body>
</html>

test.html을 작성합니다.



2. Test 작성하기

TestControllerTest.class

@WebMvcTest(value = TestController.class)
class TestControllerTest {
    @Autowired
    private MockMvc mvc;

    @Test
    public void 테스트_페이지_요청() throws Exception {
        mvc.perform(get("/test"))
                .andExpect(view().name("test"))
                .andExpect(status().isOk())
                      .andDo(print());
    }
}

위와 같이 TestController를 Test하는 클래스를 생성해보도록 하겠습니다.



결과는 위와 같이 에러가 발생하는데요, 자세히 보시면, status가 302로 나타나는 것을 볼 수 있습니다.



원인

원인은, spring security의 form login 때문입니다.

form login설정이 되어있는 경우, login을 하지 않은 상태의 유저가, 허가되지 않은 요청을 하는 경우, 로그인 페이지로 리다이렉션 됩니다.

이 때문에, 우리가 원하는 응답은 2xx이지만, 3xx로 응답이 오기때문에, 테스트에 실패하게 됩니다.



해결방법 1. 3xx redirection을 기대 결과값으로 하기

결과를 먼저 말씀드리자면, 옳바른 해법이 아닙니다.

@Test
public void 테스트_페이지_요청() throws Exception {
    mvc.perform(get("/test"))
            .andExpect(status().is3xxRedirection())
                  .andDo(print());
}

3xx로 응답값을 기대하는 경우, 테스트에는 통과를 하겠지만, 우리가 테스트하고자 하는 근본적인 기능들을 테스트 할 수가 없습니다.

당연히, 우리가 원하는 페이지는 /test 이고, 이에 대한 요청시 서버에서 제공하는 기능들은 단순히 페이지를 주는것 뿐만 아니라 여러가지 행동들을 할 수 있으며 그에 대한 테스트를 진행하는 것이 원래 우리가 원하는 테스트이기 때문입니다.



해결방법 2. @WithMockUser을 사용하기

@WithMockUser

이 어노테이션은 Spring Security에서 제공하는 어노테이션으로, Spring Mvc를 테스트할 때 사용되는 어노테이션입니다. 결과적으로, 이 어노테이션을 부여하면, 인증이 된 상태로 테스트를 진행하도록 도와줍니다(SecurityContextHolder에 UsernamePasswordAuthenticationToken가 담긴 상태를 만들어 줍니다).


@WebMvcTest(value = TestController.class)
class TestControllerTest {
    @Autowired
    private MockMvc mvc;

    @Test
    @WithMockUser
    public void 테스트_페이지_요청() throws Exception {
        mvc.perform(get("/test"))
                .andExpect(view().name("test"))
                .andExpect(status().isOk())
                .andDo(print());
    }
}

바로 어노테이션을 부여하고 테스트를 진행하도록 하겠습니다.


성공입니다.