@SpringBootTest를 사용한 testcode 작성하기

zoey·2022년 11월 29일
1

단위테스트 / 통합테스트 / 인수테스트

단위테스트 (Unit Test)

  • 단위테스트는 응용프로그램에서 테스트 가능한 가장 작은 소프트웨어를 실행하여 예상대로 동작하는지 확인하는 테스트이다
  • 소프트웨어를 개발할 때, 소프트웨어 내부 구조나 구현 방법을 고려하여 개발자 관점에서 테스트한다. 그러므로 단위테스트는 소프트웨어 내부 코드에 관련한 지식을 반드시 알고있어야 하는 화이트박스 테스트이다. 단위테스트는 TDD와 함께 할 때 특히 더 강력해진다
  • 단위 테스트는 프로젝트에 필요한 모든 기능에 대한 테스트를 각각 진행하는 것을 의미
  • 일반적으로 스프링부트에서는 ‘org.springframework.book:spring-boot-starter-test’디펜던시만으로 의존성을 모두 가질 수 있음

통합테스트 (Integration Test)

  • 통합테스트는 단위테스트보다 더 큰 동작을 달성하기 위해 여러 모듈들을 모아 이들이 의도대로 협력하는지 확인하는 테스트이다
  • 통합테스트는 단위테스트와 달리 개발자가 변경할 수 없는 부분(ex.외부 라이브러리)까지 묶어 검증할 때 사용한다. 이는 DB에 접근하거나 전체 코드와 다양한 환경이 제대로 작동하는지 확인하는데 필요한 모든 작업을 수행할 수 있다.
  • 통합테스트의 장점은 단위테스트에서 발견하기 어려운 버그를 찾을 수 있다는 점이다.
  • 통합테스트의 단점은 단위테스트보다 더 많은 코드를 테스트하기 때문에 신뢰성이 떨어질 수 있따는 점이다. 또, 어디서 에러가 발생했는지 확인하기 쉽지 않아 유지보수하기 힘들다는 점도 있다
  • 통합 테스트는 여러 기능을 조합하여 전체 비즈니스 로직이 제대로 동작하는지 확인하는 것을 의미
  • 통합 테스트의 경우, @SpringBootTest를 사용하여 진행
    • @SpringBootTest는 @SpringBootApplication을 찾아가서 모든 Bean을 로드하게 됨
    • 이 방법을 대규모 프로젝트에서 사용할 경우, 테스트를 실행할 때마다 모든 빈을 스캔하고 로드하는 작업이 반복되어 매번 무거운 작업을 수행해야 함
    • @WebMvcTest

인수테스트(Acceptance Test)

  • 인수테스트는 사용자 스토리(시나리오)에 맞춰 수행하는 테스트이다
  • 앞선 두 테스트들과 달리 비즈니스 쪽에 초점을 둔다. 프로젝트에 참여하는 사람들(ex. 기획자, 클라이언트 대표, 개발자 등)이 토의해서 시나리오를 만들고, 개발자는 이에 의거해서 코드를 작성한다. 개발자가 직접 시나리오를 제작할 수도 있지만, 다른 의사소통집단으로부터 시나리오를 받아(인수) 개발한다는 의미를 가지고 있다
  • 소프트웨어를 인수할 때, 소프트웨어 내부 구조나 구현방법을 고려하기 보다는 실제 사용자 관점에서 테스트하는 경우가 많다. 따라서, 인수테스트는 소프트웨어 내부 코드에 관심을 가지지 않는 블랙박스 테스트이다.
  • Java에서는 MockMvc, RestAssured 같은 도구를 활용하여 인수테스트를 작성할 수 있다

@SpringBootTest

  • 통합텍스트 테스트 용도로 사용됨
  • @SpringBootApplication을 찾아가 하위의 모든 Bean을 스캔하여 로드한다. 그리고 @MockBean으로 정의된 빈을 찾아서 교체한다
  • 그 후 Test용 Application Context를 만들어 Bean을 추가하고, MockBean을 찾아 교체

@Transactional

각각의 테스트함수가 종료될 때마다 트랜잭션을 rollback 해주는 어노테이션

@AutoConfigureMockMvc

spring.test.mockmvc의 설정을 로드하면서 MockMvc의 의존성을 자동으로 주입, MockMvc 클래스는 Rest API 테스트를 할 수 있는 클래스

MockMvc

  • 웹 API 테스트할 때 사용
  • 스프링 MVC 테스트의 시작점
  • HTTP GET,POST 등에 대해 API 테스트 가능
@SpringBootTest
@Transactional
@AutoConfigureMockMvc
class ReportRestControllerTest {

	@Autowired MockMvc mvc;
    @Autowired ReportRepository reportRepository;
    @Autowired ObjectMapper objectMapper = new ObjectMapper();
    
     @Test
    void createReport() throws Exception {

        //Given(테스트를 하기 위한 준비)
        String testName = "테스트";
        String testCreateUserId = "abcde@gmail.com";
        String testDescription = "테스트 리포트 description ";
        String testText = "테스트 리포트 text";
        String testWorkId = "testworkid";
        String testPrtId = "testprtid";
        String testExpId = "testexpid";

        CoreRequestDTO coreRequestDTO = new CoreRequestDTO();

        coreRequestDTO.setAccessType("create");
        LinkedHashMap reqMap = new LinkedHashMap();
        reqMap.put("name",testName);
        reqMap.put("create_user_id",testCreateUserId);
        reqMap.put("create_time", LocalDateTime.now(ZoneId.of("Asia/Seoul")));
        reqMap.put("description",testDescription);
        reqMap.put("text",testText);
        reqMap.put("work_id",testWorkId);
        reqMap.put("prt_id",testPrtId);
        reqMap.put("exp_id",testExpId);

        coreRequestDTO.setReqDetail(reqMap);

        //When(테스트 실행)
        ResultActions resultActions = mvc.perform(MockMvcRequestBuilders.post("/api/core/report")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(coreRequestDTO))
                .accept(MediaType.APPLICATION_JSON));

        //Then(검증)
        resultActions //mvc.perform()에 대한 결과 검증
                .andExpect(status().isOk())//상태가 200인지 확인
                .andExpect(jsonPath("$.resResult.name").value(testName)) //테스트로 응답받은 데이터와 response Body의 데이터가 동일한지 확인
                .andExpect(jsonPath("$.resResult.description").value(testDescription)) //테스트로 응답받은 데이터와 response Body의 데이터가 동일한지 확인
                .andExpect(jsonPath("$.resResult.text").value(testText)) //테스트로 응답받은 데이터와 response Body의 데이터가 동일한지 확인
                .andDo(print()); //요청, 응답 전체 메세지를 콘솔로 확인

    }

    @Test
    void readReport() throws Exception {

        //Given(테스트를 하기 위한 준비)
        int testReportId = 118;

        //When(테스트 실행)
        ResultActions resultActions = mvc.perform(MockMvcRequestBuilders.get("/api/core/report/{reportId}",testReportId)
                .contentType(MediaType.APPLICATION_JSON)
                .accept(MediaType.APPLICATION_JSON));

        //Then(검증)
        resultActions
                //mvc.perform() 결과 검증
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.resResult.id").value(testReportId))
                .andDo(print());

    }
    
    @Test
    void updateReport() throws Exception {

        //Given(테스트를 하기 위한 준비)
        String testReportId = "213";
        String testUpdateName = "테스트 리포트 이름 업데이트";
        String testUpdateUserId = "jh.okestro@gmail.com";
        String testUpdateDescription = "테스트 리포트 description ";
        String testUpdateText = "테스트 리포트 text";
        String testWorkId = "testworkidworkid";
        String testPrtId = "testprtidprtid";
        String testExpId = "testexpidexpid";


        CoreRequestDTO coreRequestDTO = new CoreRequestDTO();

        coreRequestDTO.setAccessType("update");
        LinkedHashMap reqMap = new LinkedHashMap();
        reqMap.put("name",testUpdateName);
        reqMap.put("update_user_id",testUpdateUserId);
        reqMap.put("update_time", LocalDateTime.now(ZoneId.of("Asia/Seoul")));
        reqMap.put("description",testUpdateDescription);
        reqMap.put("text",testUpdateText);
        reqMap.put("work_id",testWorkId);
        reqMap.put("prt_id",testPrtId);
        reqMap.put("exp_id",testExpId);

        coreRequestDTO.setReqDetail(reqMap);

        //When(테스트 실행)
        ResultActions resultActions = mvc.perform(MockMvcRequestBuilders.put("/api/core/report/{reportId}",testReportId)
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(coreRequestDTO))
                .accept(MediaType.APPLICATION_JSON));

        //Then(검증)
        resultActions //mvc.perform()에 대한 결과 검증
                .andExpect(status().isOk())//상태가 200인지 확인
                .andExpect(jsonPath("$.resResult.targetName").value(testUpdateName)) //테스트로 응답받은 데이터와 response Body의 데이터가 동일한지 확인
                .andDo(print()); //요청, 응답 전체 메세지를 콘솔로 확인

    }
    
    @Test
    void deleteReport() throws Exception {

        //Given(테스트를 하기 위한 준비)
        int reportId = 119;
        CoreRequestDTO coreRequestDTO = new CoreRequestDTO();
        coreRequestDTO.setAccessType("delete");

        //When(테스트 실행)
        ResultActions resultActions = mvc.perform(MockMvcRequestBuilders.delete("/api/core/report/{reportId}",reportId)
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(coreRequestDTO))
                .accept(MediaType.APPLICATION_JSON));
        //Then(검증)
        resultActions
                //mvc.perform()에 대한 결과 검증
                .andExpect(status().isOk())
                .andDo(print()); //요청, 응답 전체 메세지를 확인
    }
        


}//ReportRestControllerTest

1개의 댓글

comment-user-thumbnail
2022년 12월 1일

와우!~ 역시 주현님 나날이 발전하시는군요?? 앞으로 더 화이팅 해봅시다 😍

답글 달기