java/Spring
SpringBoot Controller 통합테스트
킨글
2023. 1. 28. 11:59
Front와 Backend 서버를 분리하고 싶기도 하고, 매번 개발 시마다
스프링의 실행 시간을 기다리는 게 아쉬워서 테스트 코드를 적극 활용해 보기로 하였다.
더불어 품질도 함께 챙길 수 있으니 일석삼조이다.
Annotation 추가
- @SpringBootTest : 통합 테스트를 지원하는 Annotation이다. 테스트에 필요한 대부분의 의존성을 제공.
예로 들면 @ContextConfiguration을 통해서 xml의 위치를 설정해주던 부분을 대신해줌. - @AutoConfigureMockMvc : MockMvc의 의존성을 주입해주기 위해서 사용하는 Annotation이다.
MockMvc를 활용해 get, post 등의 요청을 하기 위해서 선언. - @Transactional : 실행이 완료된 후에 결과 값을 Rollback함으로써 DB에 저장되지 않도록 해줌.
반복적인 테스트를 진행하기 위해서는 참으로 필요한 Annotation이 아닐까 싶다.
@SpringBootTest
@AutoConfigureMockMvc
@Transactional
class MemberAPITest {
@Autowired
MockMvc mvc;
@Autowired
ObjectMapper objectMapper;
...
}
테스트 코드 작성
- MemberVO : 회원가입 시에 form을 통해서 입력되는 데이터들을 미리 선언해주어 .content 부분에서 활용
- objectMapper : java Object를 json 문자열로 반환하기 위해서 사용
- status().isOk() : 요청이 성공적으로 이루어졌는지 확인하기 위해서 사용
- jsonPath : /signup 컨트롤러의 경우 성공을 하게 되면 status 를 true로 반환함.
반환되는 json 값을 검증하기 위해서 $.status를 입력해서 진행
@Test
void signup() throws Exception {
MemberVO memberVO = MemberVO.builder()
.userId("notExist_testuser")
.pwd("dz")
.build();
//when
ResultActions resultActions = mvc.perform(post("/signup")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(memberVO))
.accept(MediaType.APPLICATION_JSON))
.andDo(print());
//then
resultActions
.andExpect(status().isOk())
.andExpect(jsonPath("$.status", equalTo(true)))
;
}
Controller 코드
- signup이 성공하면 status를 true, 실패하면 false를 반환해줌.
@RestController
public class MemberAPI {
@Autowired
MemberService memberService;
@PostMapping("/signup")
public Map<String, Object> signup(@RequestBody MemberVO memberVO){
boolean signup = memberService.signup(memberVO);
Map<String, Object> resultMap = new HashMap<>();
if(signup){
resultMap.put("status", true);
}else{
resultMap.put("status", false);
}
return resultMap;
}
}
ServiceImpl 코드
- mybatis interface인 MemberDAO.save를 통해서 반환되는 값을 return 해줌.
@Service
public class MemberServiceImpl implements MemberService{
private final MemberDAO memberDAO;
@Override
public boolean signup(MemberVO memberVO) {
return memberDAO.save(memberVO);
}
}
전체 테스트 코드
- 따라하다 보니 import가 헷갈리는 경우가 있어서, 어려움을 겪는 분들을 위해서 전체 코드를 제공.
package com.dz.member.controller;
import com.dz.member.service.MemberService;
import com.dz.member.vo.MemberLoginParam;
import com.dz.member.vo.MemberVO;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.transaction.annotation.Transactional;
import static org.hamcrest.Matchers.equalTo;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest
@AutoConfigureMockMvc
@Transactional
class MemberAPITest {
@Autowired
MemberService memberService;
@Autowired
MockMvc mvc;
@Autowired
ObjectMapper objectMapper;
@Test
void login() throws Exception {
//given
MemberLoginParam memberLoginParam = MemberLoginParam.builder()
.userId("exist_testuser")
.pwd("dz")
.build();
//when
ResultActions resultActions = mvc.perform(post("/login")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(memberLoginParam))
.accept(MediaType.APPLICATION_JSON))
.andDo(print());
//then
resultActions
.andExpect(status().isOk())
.andExpect(jsonPath("$.status", equalTo(true)))
;
}
@Test
void signup() throws Exception {
MemberVO memberVO = MemberVO.builder()
.userId("notExist_testuser")
.pwd("dz")
.build();
//when
ResultActions resultActions = mvc.perform(post("/signup")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(memberVO))
.accept(MediaType.APPLICATION_JSON))
.andDo(print());
//then
resultActions
.andExpect(status().isOk())
.andExpect(jsonPath("$.status", equalTo(true)))
;
}
}