4.1 테스트 코드 개념 익히기
4.1.1 테스트 코드란?
- given-when-then 패턴
- given : 테스트 실행을 준비하는 단계
- when : 테스트를 진행하는 단계
- then : 테스트 결과를 검증하는 단계
// given-when-then 패턴의 테스트 코드 예시
@DisplayName("새로운 메뉴를 저장한다.")
@Test
public void saveMenuTest() {
// given : 메뉴를 저장하기 위한 준비 과정
final String name = "아메리카노";
final int price = 2000;
final Menu americano = new Menu(name, price);
// when : 실제로 메뉴를 저장
final long savedID = menuService. save(americano);
// then : 메뉴가 잘 추가되었는지 검증
final Menu savedMenu = menuService.findById(savedId).get();
assertThat(savedMenu.getName()).isEqualTo(name);
assertThat(savedMenu.getPrice()).isEqualTo(price);
}
4.2 스프링 부트 3와 테스트
- spring-boot-sstarter-test 스타터에는 테스트를 위한 도구들이 모여있음
<스프링 부트 스타터 테스트 목록>
- JUnit : 자바 프로그래밍 언어용 단위 테스트 프레임워크
- Spring Test & Spring Boot Test : 스프링 부트 애플리케이션을 위한 통합 테스트 지원
- AssertJ : 검증문인 어설션을 작성하는 데 사용되는 라이브러리
- Hamcrest : 표현식을 보다 이해하기 쉽게 만드는데 사용되는 Matcher 라이브러리
- Mockito : 테스트에 사용할 가짜 객체인 목 객체를 쉽게 만들고, 관리하고, 검증할 수 있게 지원하는 테스트 프레임워크
- JSONassert : JSON용 어설션 라이브러리
- JsonPath : JSON 데이터에서 특정 데이터를 선택하고 검색하기 위한 라이브러리
(이 중 JUnit과 AssertJ를 가장 많이 사용한다)
4.2.1 JUnit
- 자바 언어를 위한 단위 테스트 프레임워크
<Junit의 특징>
- 테스트 방식을 구분할 수 있는 애너티에션을 제공
- @Test 애너테이션으로 메서드를 호출할 때마다 새 인스턴스를 생성, 독립 테스트 가능
- 예상 결과를 검증하는 어설션 메서드 제공
- 사용 방법이 단순, 테스트 코드 작성 시간이 적음
- 자동 실행, 자체 결과를 확인하고 즉작적인 피드백을 제공
<Junit으로 단위 테스트 코드 만들기>
1단계. JUnitTest.java 파일 생성
- @DisplayName : 테스트 이름 명시 기능
- @Test : 해당 애너테이션이 붙은 메서드를 테스트 수행
- 각 테스트를 실행할 때마다 실행 객체를 만들고, 종료와 동시에 삭제
- assertEquels() : 첫 번째 인수 = 기대하는 값 / 두 번째 인수 = 실제로 검증할 값.
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 // 테스트 메서드
public void junitTest() {
int a = 1;
int b = 2;
int sum = 3;
Assertions.assertEquals(a + b, sum); // 값이 같은지 확인
}
}
3단계. 실패 테스트 케이스
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class JUnitTest {
@DisplayName("1 + 3는 4이다") // 실패 테스트 메서드
@Test // 테스트 메서드
public void junitFailedTest() {
int a = 1;
int b = 3;
int sum = 3;
Assertions.assertEquals(a + b, sum);
}
}
4단계. 다양한 애너테이션 사용해보기
- @BeforeAll : 전체 테스트를 시작하기 전에 처음으로 한 번만 실행 (한 번만 실행 -> static으로 선언 O)
(ex. 데이터 베이스 연결, 테스트 환경 초기화)
- @BeforeEach : 테스트 케이스를 시작하기 전에 매번 실행 (매번 실행 -> static으로 선언 X)
(ex. 테스트 메서드에서 사용하는 객체 초기화, 테스트에 필요한 값 미리 넣을 때 사용)
- @AfterAll : 전체 테스트를 마치고 종료하기 전에 한 번만 실행 (한 번만 실행 -> static으로 선언 O)
(ex. 데이터베이스 연결을 종료할 때, 공통적으로 사용하는 자원을 해제할 때)
- @AfterEach : 각 테스트 케이스를 종료하기 전에 매번 실행 (매번 실행 -> static으로 선언 X)
(ex. 테스트 이후 특정 데이터를 삭제해야하는 경우 사용)
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 클래스 레벨 설정 |
@BeforeEach 메서드 레벨 설정 |
@Test 테스트 실행 |
@AfterEach 메서드 레벨 정리 |
@AfterAll 클래스 레벨 정리 |
(@AfterEach -> @BeforeEach 테스트 개수만큼 반복)
- AssertJ로 검증문 가독성 높이기
// 서로 같은 코드
Assertion.assertEquals(a + b, sum);
// ▼
assertThat(a + b).isEqualTo(sum);
메서드 이름 | 설명 | 메서드 이름 | 설명 |
isEqualTo(A) | A 값과 같은지 검증 | isEmpty() | 비어있는 값인지 검증 |
isNotEqualTo(A) | A 값과 다른지 검증 | isNotEmpty() | 비어있지 않은 값인지 검증 |
contains(A) | A 값을 포함하는지 검증 | isPositive() | 양수인지 검증 |
doesNoContain(A) | A 값을 포함하지 않는지 검증 | isNegative() | 음수인지 검증 |
startsWith(A) | 접두사가 A인지 검증 | isGreaterThan(1) | 1보다 큰 값인지 검증 |
endsWith(A) | 접미사가 A인지 검증 | isLessThan(1) | 1보다 작은 값인지 검증 |
4.3 제대로 테스트 코드 작성해보기
1단계. TestControllerTest.java 파일 생성
2단계. TestControllerTest.java 파일 작성
package me.kyungsoolee.springbootdeveloper;
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를 생성하고 자동으로 구성
3단계. 로직 테스트 코드 작성
package me.kyungsoolee.springbootdeveloper;
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.*;
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
final ResultActions result = mockMvc.perform(get(url) // (1) 요청을 전송하는 메서드
.accept(MediaType.APPLICATION_JSON)); // (2) 요청을 보낼 때 무슨 타입으로 응답을 받을지 결정하는 메서드
// then
result
.andExpect(status().isOk()) // (3) 응답을 검증하는 메서드
// (4) 응답 값을 가져오고, 응답의 0번째 값이 DB에서 저장한 값과 같은지 확인하는 메서드
.andExpect(jsonPath("$[0].id").value(savedMember.getId()))
.andExpect(jsonPath("$[0].name").value(savedMember.getName()));
}
}
- Given : 멤버를 저장
- When : 멤버 리스트를 조회하는 API 호출
- Then : 응답 코드가 200 OK이고, 반환받은 값 중에 0번째 요소의 id와 name이 저장된 값과 같은지 확인
코드 | 매핑 메서드 | 설명 |
200 OK | isOK() | HTTP 응답 코드가 200 OK인지 검증 |
201 Created | isCreated() | HTTP 응답 코드가 201 Created인지 검증 |
400 Bad Request | isBadRequest() | HTTP 응답 코드가 400 Bad Request인지 검증 |
403 Forbidden | isForbidden() | HTTP 응답 코드가 403 Forbidden인지 검증 |
404 Not Found | isNotFound() | HTTP 응답 코드가 404 Not Found인지 검증 |
400번대 응답 코드 | is4xxClientError() | HTTP 응답 코드가 400번대 응답 코드인지 검증 |
500 Internal Server Error | isInternalServerError() | HTTP 응답 코드가 500 Internal Server Error인지 검증 |
500번대 응답 코드 | is5xxServerError() | HTTP응답 코드가 500번대 응답 코드인지 검증 |
4단계. 실행 확인
<4장 핵심요약> 1. 테스트 코드를 작성여 기능을 검증할 수 있다. 1-1. given : 테스트 준비 1-2. when : 테스트 진행 1-3. then : 테스트 결과 검증 2. JUnit는 단위 테스트를 할 때 사용하는 자바 테스트 프레임워크이다. -> @Before~@After 애너테이션 생성주기 공부! 3. AssertJ는 JUnit과 함께 사용하는 가독성을 높여주는 라이브러리이다. |
'Spring boot > 스프링 부트 3 백엔드 개발자 되기(자바편)' 카테고리의 다른 글
[6장] 블로그 기획하고 API 만들기(2) (0) | 2023.07.25 |
---|---|
[5장] 데이터베이스 조작이 편해지는 ORM (0) | 2023.07.25 |
[3장] 스프링 부트3 구조 이해하기 (0) | 2023.07.25 |
[2장] 스프링 부트 3 시작하기(2) - 스프링 부트 3 예제 만들기 (0) | 2023.07.25 |
[2장] 스프링 부트 3 시작하기(1) - 스프링 콘셉트 (0) | 2023.07.25 |