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)))
                ;
    }
}