1. 단위 테스트
- 기능 테스트 : 애플리케이션을 사용하는 사용자 입장에서 애플리케이션이 제공하는 기능이 올바르게 동작하는지를 테스트
- 통합 테스트 : Controller의 API를 호출하는 테스트 코드를 작성한 후 서비스 계층과 데이터 액세스 계층을 거쳐 DB에 실제로 접속해서 기대했던 대로 동작 하는지를 테스트
- 슬라이스 테스트 : 계층별 테스트
- 단위 테스트
- 메서드 단위 테스트
- 주로 서비스 계층의 비즈니스 로직을 테스트
1) 단위 테스트를 위한 F.I.R.S.T 원칙
- Fast(빠르게)
- Independent(독립적으로)
- 실행되는 테스트 케이스의 순서에 상관없이 정상적인 실행이 보장 되어야 한다.
- Repeatable(반복 가능하도록)
- 어떤 환경에서 실행하든 반복해서 같은 결과가 나와야 한다.
- Self-validating(스스로 검증 되도록)
- 테스트 케이스 스스로 결과가 성공인지 실패인지 보여주어야 한다.
- Timely(시기적절하게)
- 테스트하려는 기능 구현을 하기 직전에 작성해야 한다.
2) given - when - then
- given
- 테스트에 필요한 전제 조건
- 테스트 대상에 전달되는 값 포함
- when
- then
- 결과 검증
- 예상 값과 테스트 대상 메서드 실행 결과를 비교
2. JUnit
- Java 언어로 만들어진 애플리케이션을 테스트하기 위한 오픈 소스 테스트 프레임워크
- Spring Boot Intializr를 이용해서 프로젝트를 생성하면 기본적으로
testImplementation>'org.springframework.boot:spring-boot-starter-test' 스타터가 포함되며, JUnit도 포함
1) Assertion
(1) assertEquals()
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class HelloJUnitTest {
@Test
@DisplayName("AssertionEqual() Test")
public void assertionTest() {
String expected = "Hello";
String actual = "Hello";
assertEquals(expected, actual);
}
}
- (1)DisplayName("") : 테스트 케이스 실행 시 실행 결과 창에 표시되는 이름 지정
- (2) 기대값과 실제 결과값이 일치하는지 검증
(2) assertNotNull() : null 여부 테스트
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class HelloJUnitTest {
@Test
@DisplayName("AssertionNull() Test")
public void assertionNotNullTest() {
String currencyName = CryptoCurrency.map.get("ETH");
assertNotNull(currencyName, "should be not null");
}
}
public class CryptoCurrency {
public static Map<String, String> map = new HashMap<>();
static {
map.put("BTC", "Bitcoin");
map.put("ETH", "Ethereum");
map.put("ADA", "ADA");
map.put("POT", "Polkadot");
}
}
- (1) 대상 객체가 null인지 테스트
- (테스트 대상 객체, "테스트 실패 시 표시할 메세지")
(3) assertThrows() : 예외 테스트
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class HelloJUnitTest {
@Test
@DisplayName("throws NullPointerException")
public void assertionExceptionTest() {
assertThrows(NullPointerException.class, () ->
CryptoCurrency.map.get("XRD").toUpperCase());
}
}
public class CryptoCurrency {
public static Map<String, String> map = new HashMap<>();
static {
map.put("BTC", "Bitcoin");
map.put("ETH", "Ethereum");
map.put("ADA", "ADA");
map.put("POT", "Polkadot");
}
}
- (1) 첫 번째 파라미터에 발생이 기대되는 예외 클래스를 입력하고, 두 번째 파라미터인 람다 표현식에서는 테스트 대상 메서드를 호출
- 해당 예외 발생시 성공
(4) @BeforeEach : 전처리
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class BeforeEachTest1 {
@BeforeEach
public void init() {
System.out.println("pre-processing before each test case");
}
@Test
@DisplayName("Test1")
public void test1() {
System.out.println("test1");
}
@Test
@DisplayName("Test2")
public void test2() {
System.out.println("test2");
}
}
- @BeforeEach가 붙은 메서드는 테스트 케이스가 각각 실행될때마다 먼저 실행
(5) @BeforeAll : 전처리
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.util.HashMap;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
public class BeforeAllTest {
private static Map<String, String> map;
@BeforeAll
public static void init() {
map = new HashMap<>();
map.put("BTC", "Bitcoin");
map.put("ETH", "Ethereum");
map.put("ADA", "ADA");
map.put("POT", "Polkadot");
map.put("XRP", "Ripple");
System.out.println("initialize Crypto Currency map");
}
@Test
@DisplayName("Test1")
public void test1() {
assertDoesNotThrow(() -> map.get("XRP").toUpperCase());
}
@Test
@DisplayName("Test2")
public void test2() {
assertDoesNotThrow(() -> map.get("ADA").toUpperCase());
}
}
- 테스트 케이스가 실행되기 전 한번만 초기화 작업 실행
- 테스트 케이스들에서 초기화한 값 사용 가능
2) Assumption
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assumptions.*;
import static org.junit.jupiter.api.Assertions.*;
public class AssumptionTest {
@Test
@DisplayName("Assumption Test")
public void assumptionTest() {
assumeTrue(System.getProperty("os.name").startsWith("Windows"));
System.out.println("execute?");
assertTrue(processOnlyWindowsTask());
}
private boolean processOnlyWindowsTask() {
return true;
}
}
- 특정 환경에만 테스트 케이스 실행
- (1) assumpTrue() 파라미터의 값이 true이면 나머지 아래 로직 실행
3) Hamcrest
- JUnit 기반의 단위 테스트에서 사용할 수 있는 Assertion Framework
- 자연스러운 Matcher로 가독성 향상
- 위에서 작성한 JUnit Assertion을 사용한 단위 테스트에 적용
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.hamcrest.Matchers.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.hamcrest.MatcherAssert.assertThat;
public class HelloJUnitTest {
@Test
@DisplayName("AssertionEqual() Test")
public void assertionTest() {
String expected = "Hello";
String actual = "Hell";
assertEquals(expected, actual);
assertThat(actual, is(equalTo(expected)));
}
@Test
@DisplayName("AssertionNull() Test")
public void assertionNotNullTest() {
String currencyName = CryptoCurrency.map.get("ETH");
assertNotNull(currencyName, "should be not null");
assertThat(currencyName, is(notNullValue()));
assertThat(currencyName, is(nullValue()));
}
@Test
@DisplayName("throws NullPointerException")
public void assertionExceptionTest() {
Throwable actualException = assertThrows(NullPointerException.class,
() -> CryptoCurrency.map.get("XRP").toUpperCase());
assertThat(actualException.getClass(), is(NullPointerException.class));
}
}