JAVA/소박한그룹 프로젝트

[Spring][TDD] MockMvc를 이용한 Controller Test

kth990303 2021. 9. 16. 20:13
반응형

우리가 API를 개발하거나, 컨트롤러에서 view로 model을 넘겨줄 때, 또는 spring security로 로그인하여 권한이 있을 때만 service logic을 테스트할 수밖에 없는 경우가 분명히 존재한다.

 

이럴 땐, 직접 실행하여 테스트해보는 수밖에 없을까?

그렇지 않다. 임의의 가짜 객체 Mock을 만들어서 가상으로 테스트해보면 된다!

 

이 때 사용되는 방법엔 @WebMvcTest, @AutoConfigureMockMvc가 있는데,

둘의 기능은 거의 비슷하나, @AutoConfigureMockMvc가 전자와는 다르게 @Service, @Repository가 붙은 객체들도 모두 메모리에 올린다는 차이점이 존재한다. 또한, @WebMvcTest는 @SpringBootTest의 MockMvc와 충돌할 수 있어 @SpringBootTest와 같이 사용할 수 없다고 한다.

 

자세한 사용법은 아래 블로그에서 볼 수 있다.

https://elevatingcodingclub.tistory.com/61

 

[SpringBoot] Test(2) MockMvc를 이용해서 테스트하기(@WebMvcTest, @AutoConfigureMockMvc)

Mock - Mock이라는 단어를 사전에서 찾아보면 '테스트를 위해 만든 모형'을 의미한다. - 테스트를 위해 실제 객체와 비슷한 모의 객체를 만드는 것을 모킹(Mocking)이라고 하며, 모킹한 객체를 메모리

elevatingcodingclub.tistory.com

 

이번 시간에는 @AutoConfigureMockMvc를 이용하여 MemberController의 Test코드를 만들어줄 것이다.

MemberController 클래스에서 Alt + Enter를 눌러주어 자동으로 Test Code 틀을 만들어주게 해주자.

 

참고로 이번 시간에 해결한 나의 깃헙이슈는 아래 이슈와 같다.

https://github.com/kth990303/BOJStudyList/issues/6

 

TDD 코드를 작성해주세요. · Issue #6 · kth990303/BOJStudyList

9월 17일까지 아래 두 가지를 추가해주세요. 테스트 코드 완성하기 스프링시큐리티 Test code 추가해보기

github.com

 

1. 메인화면

  • 로그인하지 않은 유저도 메인화면을 제대로 열람할 수 있는지

get method로 "/" 라우터로 접속해준다. 이 때, index.html (index view)가 잘 열람되는지 확인한다.

어노테이션은 @WithAnonymousUser를 사용한다. 권한이 없는 유저로 접속해야할 땐 이 어노테이션을 붙여주면 된다.

    @Test
    @WithAnonymousUser
    public void 메인화면() throws Exception{
        mockMvc.perform(get("/"))
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(view().name("index"));
    }

2. 로그인, 로그아웃

  • 일반적인 테스트 코드에서는 스프링 시큐리티를 활용한 권한 체크가 어렵다. MockMvc를 이용하여 Controller 단에서 login 테스트를 하면 쉽게 확인이 가능하다.
    @Test
    @Transactional
    public void 로그인성공() throws Exception{
        String username = "test";
        String tier="Silver I";
        String email="test@gmail.com";
        String password = "12345";

        MemberDto member=new MemberDto(username, tier, email, password);
        memberService.join(member);
        // 성공 TEST
        mockMvc.perform(formLogin().user(member.getName()).password(password))
                .andDo(print())
                .andExpect(authenticated());
    }

    @Test
    @Transactional
    public void 로그인실패() throws Exception{
        String username = "test";
        String tier="Silver I";
        String email="test@gmail.com";
        String password = "12345";

        MemberDto member=new MemberDto(username, tier, email, password);
        memberService.join(member);
        // 실패 TEST
        mockMvc.perform(formLogin().user(member.getName()).password("123456"))
                .andExpect(unauthenticated());
    }

 

3. 정보수정

  • 정보수정 메소드에는 관리자인지, 일반 유저인지 체크하는 spring security 메소드가 존재한다. 따라서 일반 테스트코드에선 진행하기 어려우나, mockMvc와 함께라면 쉽게 가능하다.
    @Test
    @WithMockUser(username = "test", roles = "USER")
    @Transactional
    public void 정보수정() throws Exception{
        String username = "test";
        String tier="Silver I";
        String email="test@gmail.com";
        String password = "12345";

        MemberDto member=new MemberDto(username, tier, email, password);
        memberService.join(member);


        Long id = memberService.findIdByName(member.getName());
        MemberDto member2=new MemberDto(username, "Bronze V", email, password);

        Long changeId = memberService.edit(id, member2);
        MemberDto memberDto = memberService.findById(changeId);
        Assertions.assertThat(memberDto.getTier()).isEqualTo(member2.getTier());

    }

주의할 점은, DTO이기 때문에 assertThat에서 member.getTier()과 member2.getTier()이 같은지 체크하면 안된다. member는 변환되기 전 Entity의 DTO이기 때문이다.


전체 코드

@SpringBootTest
@AutoConfigureMockMvc
class MemberControllerTest {
    @Autowired
    MockMvc mockMvc;

    @Autowired
    MemberService memberService;

    @Retention(RetentionPolicy.RUNTIME)
    @WithMockUser(username = "kth990303", roles = "ADMIN")
    public @interface WithAdminUser{}

    @Retention(RetentionPolicy.RUNTIME)
    @WithMockUser(username="choisk0531", roles = "USER")
    public @interface WithUser{}


    @Test
    @WithAnonymousUser
    public void 메인화면() throws Exception{
        mockMvc.perform(get("/"))
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(view().name("index"));
    }

    @Test
    @Transactional
    public void 로그인성공() throws Exception{
        String username = "test";
        String tier="Silver I";
        String email="test@gmail.com";
        String password = "12345";

        MemberDto member=new MemberDto(username, tier, email, password);
        memberService.join(member);
        // 성공 TEST
        mockMvc.perform(formLogin().user(member.getName()).password(password))
                .andDo(print())
                .andExpect(authenticated());
    }

    @Test
    @Transactional
    public void 로그인실패() throws Exception{
        String username = "test";
        String tier="Silver I";
        String email="test@gmail.com";
        String password = "12345";

        MemberDto member=new MemberDto(username, tier, email, password);
        memberService.join(member);
        // 실패 TEST
        mockMvc.perform(formLogin().user(member.getName()).password("123456"))
                .andExpect(unauthenticated());
    }

    @Test
    @WithUser
    public void 로그아웃() throws Exception{
        mockMvc.perform(get("/logout"))
                .andDo(print())
                .andExpect(redirectedUrl("/"));
    }

    @Test
    @WithUser
    public void 내정보() throws Exception{
        mockMvc.perform(get("/myInfo"))
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(view().name("myInfo"));
    }

    @Test
    @WithAnonymousUser
    public void 회원가입() throws Exception{
        // 회원가입 창으로 잘 가지는지
        // 로직 test는 serviceTest에서
        mockMvc.perform(get("/createMemberForm"))
                .andDo(print())
                .andExpect(view().name("createMemberForm"));
    }

    @Test
    @WithMockUser(username = "test", roles = "USER")
    @Transactional
    public void 정보수정() throws Exception{
        String username = "test";
        String tier="Silver I";
        String email="test@gmail.com";
        String password = "12345";

        MemberDto member=new MemberDto(username, tier, email, password);
        memberService.join(member);


        Long id = memberService.findIdByName(member.getName());
        MemberDto member2=new MemberDto(username, "Bronze V", email, password);

        Long changeId = memberService.edit(id, member2);
        MemberDto memberDto = memberService.findById(changeId);
        Assertions.assertThat(memberDto.getTier()).isEqualTo(member2.getTier());

    }
}

아래 github 주소에서 전체 코드를 볼 수 있다.

https://github.com/kth990303/BOJStudyList

 

GitHub - kth990303/BOJStudyList: BOJ 그룹스터디 회원들의 공간을 만들자~

BOJ 그룹스터디 회원들의 공간을 만들자~. Contribute to kth990303/BOJStudyList development by creating an account on GitHub.

github.com

 

반응형