우리가 API를 개발하거나, 컨트롤러에서 view로 model을 넘겨줄 때, 또는 spring security로 로그인하여 권한이 있을 때만 service logic을 테스트할 수밖에 없는 경우가 분명히 존재한다.
이럴 땐, 직접 실행하여 테스트해보는 수밖에 없을까?
그렇지 않다. 임의의 가짜 객체 Mock을 만들어서 가상으로 테스트해보면 된다!
이 때 사용되는 방법엔 @WebMvcTest, @AutoConfigureMockMvc가 있는데,
둘의 기능은 거의 비슷하나, @AutoConfigureMockMvc가 전자와는 다르게 @Service, @Repository가 붙은 객체들도 모두 메모리에 올린다는 차이점이 존재한다. 또한, @WebMvcTest는 @SpringBootTest의 MockMvc와 충돌할 수 있어 @SpringBootTest와 같이 사용할 수 없다고 한다.
자세한 사용법은 아래 블로그에서 볼 수 있다.
https://elevatingcodingclub.tistory.com/61
이번 시간에는 @AutoConfigureMockMvc를 이용하여 MemberController의 Test코드를 만들어줄 것이다.
MemberController 클래스에서 Alt + Enter를 눌러주어 자동으로 Test Code 틀을 만들어주게 해주자.
참고로 이번 시간에 해결한 나의 깃헙이슈는 아래 이슈와 같다.
https://github.com/kth990303/BOJStudyList/issues/6
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