Server/Spring Boot

스프링 부트3 테스트

은 딩 2023. 10. 31. 19:43

 

 

 

테스트

  • 테스트 코드는 작성한 코드가 의도대로 잘 동작하고 예상치 못한 문제가 없는지 확인할 목적으로 작성하는 코드
  • test 디렉터리에서 작업
  • given(테스트 실행 준비) - when(테스트 진행) - then(테스트 결과 검증) 단계로 진행

JUnit

  • 자바 언어를 위한 단위 테스트 프레임워크
  • 단위테스트란? 작성한 코드가 의도대로 작동하는지 작은 단위로 검증하는 것
  • JUnit 특징
    • @Test 애너테이션으로 메서드를 호출할 때마다 새 인스턴스를 생성, 독립 테스트 가능
    • 예상 결과를 검증하는 assertion method 제공
    • 사용 방법 단순, 테스트 코드 작성 시간 적음
    • 자동 실행, 자체 결과를 확인하고 즉각적인 피드백 제공

JUnit으로 단위 테스트 코드 만들기

1. test 디렉터리에 JUnitTest 파일을 만든다

2. 코드 넣고 실행

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

public class JUnitTest {
    @DisplayName("1 + 2는 3이다") // test name
    @Test // test method
    public void junitTest() {
        int a = 1;
        int b = 2;
        int sum = 3;

        Assertions.assertEquals(a+b, sum); // 값이 같은지 확인
    }
}

- JUnit은 테스트를 실행할 때마다 테스트를 위한 실행 객체를 만들고 테스트가 종료되면 실행 객체를 삭제

- assertEquals() 메서드는 첫 번째 인수에는 기대하는 값, 두 번째 인수에는 실제로 검증할 값 넣기

 

실행을 하면 @DisplayName 애너테이션에 썼던 test 이름으로 뜨는 것을 확인할 수 있다

 

3. 만약 테스트가 실패한다면?????

public class JUnitTest {
    @DisplayName("1 + 3는 4이다") // test name
    @Test // test method
    public void junitTest() {
        int a = 1;
        int b = 3;
        int sum = 3;

        Assertions.assertEquals(a+b, sum); // 값이 같은지 확인
    }
}

- 테스트가 실패했다는 표시와 함께 기댓값과 실제로 받은 값을 비교해서 알려준다.

- JUnit은 테스트 케이스가 하나라도 실패하면 전체 테스트를 실패한 것으로 보여준다.

 

JUnit의 애너테이션

import org.junit.jupiter.api.*;

public class JUnitCycleTest {
    @BeforeAll // 전체 테스트를 시작하기 전, 1회 실행하므로 메서드는 static으로 선언
    static void beforeAll(){
        System.out.println("@BeforeAll");
    }
    
    @BeforeEach // 테스트 케이스를 시작하기 전마다 실행
    public void beforeEach(){
        System.out.println("@BeforeEach");
    }
    
    @Test
    public void test1(){
        System.out.println("test1");
    }
    
    @Test
    public void test2(){
        System.out.println("test2");
    }

    @Test
    public void test3(){
        System.out.println("test3");
    }
    
    @AfterAll // 전체 테스트를 마치고 종료하기 전, 1회 실행하므로 메서드는 static으로 선언
    static void afterAll(){
        System.out.println("@AfterAll");
    }
    
    @AfterEach // 테스트 케이스를 종료하기 전마다 실행
    public void afterEach(){
        System.out.println("@AfterEach");
    }
    
}

@BeforeAll

- 테스트를 시작하기 전 처음으로 한 번만 실행

- ex) DB를 연결해야 하거나 테스트 환경 초기화

- 한 번만 호출되어야 하기 때문에 static 선언

 

@BeforeEach

- 테스트 케이스를 시작하기 전에 매번 실행

- ex) 테스트 메서드에서 사용하는 객체를 초기화하거나 테스트에 필요한 값을 미리 넣을 때 사용

- 각 인스턴스에 대해 메서드를 호출해야 하므로 static XXX

 

@AfterAll

- 전체 테스트를 마치고 종료하기 전에 한 번만 실행

- ex) DB 연결 종료, 공통적으로 사용하는 자원을 해제할 때 사용

- 한 번만 호출되어야 하기 때문에 static 선언

 

@AfterEach

- 각 테스트 케이스를 종료하기 전 매번 실행

- ex) 테스트 이후 특정 데이터를 삭제해야 하는 경우

- static XXX

 

 

실행 결과

 

제대로 된 테스트 코드 작성

1. TestController.java 파일에서 클래스 이름을 클릭하고 Alt + Enter을 누르면 [Create Test]가 나타난다.

이 파일은 test/java/패키지 아래에 생성된다

 

2. 생성된 파일에 코드 넣기

package com.example.springstarter;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
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.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest // 테스트용 애플리케이션 컨텍스트 생성
@AutoConfigureMockMvc // MockMvc 생성
class TestControllerTest {
    @Autowired
    protected MockMvc mockMvc;
    
    @Autowired
    private WebApplicationContext context;
    
    @Autowired
    private MemberRepository memberRepository;
    
    @BeforeEach // 테스트 실행 전 실행하는 코드
    public void mockMvcSetUp() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(context)
                .build();
    }
    
    @AfterEach // 테스트 실행 후 실행하는 메서드
    public void cleanUp() {
        memberRepository.deleteAll();
    }

}

 

@SpringBootTest

- 메인 애플리케이션 클래스에 추가하는 애너테이션인 @SpringBootApplication이 있는 클래스를 찾고 그 클래스에 포함되어 있는 빈을 찾은 다음 테스트용 애플리케이션 컨텍스트라는 것을 만든다.

 

@AutoConfigureMockMvc

- MockMvc를 생성하고 자동으로 구성하는 애너테이션

- 애플리케이션을 서버에 배포하지 않고도 테스트용 MVC 환경을 만들어 요청 전송, 응답 기능을 제공하는 유틸리티 클래스

- 즉, 컨트롤러를 테스트할 때 사용되는 클래스

 

@BeforeEach

- 테스트를 실행하기 전에 실행하는 메서드에 적용하는 애너테이션

- 여기서는 MockMvc를 설정해준다

 

@AfterEach

- 테스트를 실행한 이후에 실행하는 메서드에 적용하는 애너테이션

- 여기서는 member 테이블에 있는 데이터들을 모두 삭제

 

 

3. 모두 작성하면 테스트 코드 작성 완료. 이제 TestController의 로직을 테스트하는 코드 추가

package com.example.springstarter;


import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
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.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;


@SpringBootTest // 테스트용 애플리케이션 컨텍스트 생성
@AutoConfigureMockMvc // MockMvc 생성
class TestControllerTest {
    @Autowired
    protected MockMvc mockMvc;

    @Autowired
    private WebApplicationContext context;

    @Autowired
    private MemberRepository memberRepository;

    @BeforeEach // 테스트 실행 전 실행하는 코드
    public void mockMvcSetUp() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(context)
                .build();
    }

    @AfterEach // 테스트 실행 후 실행하는 메서드
    public void cleanUp() {
        memberRepository.deleteAll();
    }

    // -----
    @DisplayName("getAllMembers: 아티클 조회에 성공한다.")
    @Test
    public void getAllMembers() throws Exception{
        // given : 멤버 저장
        final String url = "/test";
        Member savedMember = memberRepository.save(new Member(1L, "홍길동"));

        // when : 멤버 리스트를 조회하는 API 호출
        final ResultActions result = mockMvc.perform(get(url) // 1
                // 1의 perform() 메서드는 요청을 전송하는 역할, 결과로 ResultActions 객체 받음
                // ResultActions 객체는 반환값을 검증하고 확인하는 andExpect() 메서드 제공
                .accept(MediaType.APPLICATION_JSON)); // 2
                // accept() 메서드는 요청을 보낼 때 무슨 타입으로 응답을 받을지 결정하는 메서드
                // ex) JSON, XML ...


        // then : 응답 코드가 200 OK이고, 반환받은 값 중에 0번째 요소의 id와 name이 저장된 값과 같은지 확인
        result
                .andExpect(status().isOk()) // 3
                // andExpect 메서드는 응답을 검증
                // TestController에서 만든 API는 응답으로 200을 반환하므로 isOK를 통해 응답코드 200인지 확인

                // 4 응답의 0번째 값이 DB에서 저장한 값과 같은지 확인
                .andExpect(jsonPath("$[0].id").value(savedMember.getId()))
                .andExpect(jsonPath("$[0].name").value(savedMember.getName()));
                // jsonPath("$[0].${필드명}")은 JSON 응답값의 값을 가져오는 역할을 하는 메서드
                // 0번째 배열에 들어있는 객체의 id, name 값을 가져오고 저장된 값과 같은지 확인


    }

}

 

 


Reference

<스프링 부트 3 백엔드 개발자 되기: 자바 편> 저자 : 신선영