단위테스트는 작성한 코드가 의도대로 작동하는지 작은 단위로 검증하는 것. 이때 단위는 보통 메서드 단위
특징
테스트 방식을 구분할 수 있는 애너테이션 제공
@Test로 메서드를 호출할 때마다 새 인스턴스 생성, 독립 테스트 가능
예상 결과를 검증하는 Assertions 메서드 제공
사용방법이 단순하며 테스트 코드 작성 시간이 짧다.
자동실행, 자체 결과를 확인하고 즉각적 피드백 제공
JUnitTestCycle
package com.junseok.springbootdevelop.test;
import org.junit.jupiter.api.*;
public class JUnitTestCycle {
@BeforeAll // 전체 테스트 시작하기전 1회 실행 static
static void beforAll() {
System.out.println("@BeforeAll");
}
@BeforeEach // 테스트 케이스를 시작하기 전 마다 실행
public void beforEach() {
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
// test1
// @AfterEach
// @BeforeEach
// test2
// @AfterEach
// @BeforeEach
// test3
// @AfterEach
// @AfterAll
@BeforeAll
전체 테스트를 시작하기 전에 처음으로 딱 한번만 실행한다. 예를들어 데이터베이스를 연결하거나 테스트 환경을 초기화 할때 사용.전체 테스트 실행 주기에서 한 번만 호출해야 하므로 메서드는 static 선언
@BeforeEach
테스트 케이스를 시작하기 전에 매번 실행. 예를들어 테스트 메서드에서 사용하는 객체를 초기화 하거나 테스트에 필요한 값을 미리 넣을 때 사용. 각 인스턴스에 대해 메서드를 호출해야 하므로 메서드는 static이 아니어야 한다.
@AfterAll
전체 테스트를 마치고 종료하기 전에 한 번만 실행한다. 예를들어 데이터 베이스 연결을 종료하거나 공통적으로 사용하는 자원을 해제할 때 사용. 전체 테스트 실행 주기에서 한 번만 호출되어야 하므로 메서드는 static 선언
@AfterEach
각 테스트 케이스를 종료하기 전 매번 실행. 예를들어 테스트 이후 특정 데이터를 삭제 해야할 떄 사용한다. 메서드는 static이 아니어야 한다.
AssertJ
JUnit과 함께 사용해 검증문의 가독성을 높여주는 라이브러리이다.
Assertions.assertEquals(a+b, sum);
// 기대값과 비교값이 잘 구분되지 않음
assertThat(a + b).isEqualTo(sum);
// 명확하게 코드를 읽을수 있어 헷갈리지 않는다.
// 지금 예시는 별거 아닐 수 있는데 프로젝트 규모가 커질수록 가독성은 중요한 문제
자주사용하는 메서드
isEqualTo(A)
A 값과 같은지 검증
isNotEqualTo(A)
A 값과 다른지 검증
contains(A)
A 값을 포함 하는지 검증
doesNotContain(A)
A 값을 포함하지 않는지 검증
startWith(A)
접두사가 A인지 검증
endsWith(A)
접미사가 A인지 검증
isEmpty()
비어있는 값인지 검증
isNotEmpty()
비어있지 않은 값인지 검증
isPositive()
양수인지 검증
isNegative()
음수인지 검증
isGreaterThan(1)
1보다 큰 값인지 검증
isLessThan(1)
1보다 작은 값인지 검증
Example
package com.junseok.springbootdevelop.test;
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 // SpringBootApplication이 있는 클래스를 찾고 그 클래스에 포함되어 있는 빈을 찾은 뒤에 테스트용 애플리케이션 컨텍스트생성
@AutoConfigureMockMvc // MockMvc를 생성하고 자동으로 구성, 테스트용 MVC 환경을 만들어 요청,전송,응답 제공 // 컨트롤러 테스트
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)
.accept(MediaType.APPLICATION_JSON));
// then 테스트 검증
result
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].id").value(savedMember.getId()))
.andExpect(jsonPath("$[0].name").value(savedMember.getName()));
}
}