JUnit
JUnit은 자바에서 단위 테스트를 작성하고 실행하기 위한 프레임 워크입니다.
JUnit은 개발자가 테스트 케이스를 작성하고 실행하는 과정을 자동화하여 더욱 쉽고 효율적으로 테스트를 수행할 수
있도록 도와줍니다.
JUnit 기본 작성법
✔ JUnit을 사용한 테스트 케이스의 기본 구조
JUnit을 사용하는 테스트 케이스의 기본 구조는 굉장히 심플합니다.
import org.junit.jupiter.api.Test;
public class JunitDefaultStructure {
// (1)
@Test
public void test1() {
// 테스트하고자 하는 대상에 대한 테스트 로직 작성
}
// (2)
@Test
public void test2() {
// 테스트하고자 하는 대상에 대한 테스트 로직 작성
}
// (3)
@Test
public void test3() {
// 테스트하고자 하는 대상에 대한 테스트 로직 작성
}
}
위 예시 코드는 테스트 케이스에 JUnit을 적용하는 기본 구조 입니다.
작성 방법이 매우 심플합니다. (1), (2), (3)과 같이 애플리케이션에서 테스트하고자 하는 대상(Target)이 있으면
public void test1(){…} 같은 void 타입의 메서드 하나 만들고, @Test 애너테이션을 추가해 줍니다.
그리고 그 내부에 테스트하고자 하는 대상 메서드에 대한 테스트 로직을 작성해 주면 됩니다.
✔ Assertion 메서드 사용하기
Assertion은 '예상한 결과 값이 참(True)이길 바라는 논리적인 표현'입니다.
Assertion의 의미가 이해하기 힘들다면 '검증한다'라는 표현을 같이 생각하면 될것 같습니다.
- assertEquals() 메서드 사용
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class HelloJUnitTest {
@DisplayName("Hello JUnit Test") // (1)
@Test
public void assertionTest() {
String expected = "Hello, JUnit";
String actual = "Hello, JUnit";
assertEquals(expected, actual); // (2)
}
}
위 코드 예시는 JUnit에서 assertEquals() 메서드를 사용하는 예입니다.
(1) @DisplayName 어노테이션을 사용하면 실행 결과 창에 표시되는 이름을 지정할 수 있습니다.
(2) assertEquals() 메서드를 사용하면 기대하는 값과 실제 결과 값이 같은지를 검증할 수 있습니다.
기대하는 문자열(expected)과 실제 결과 값(autual)이 일치하는지를 검증하고 있습니다.
이제 해당 테스트를 실행해야 합니다.

화면에 나와있는 (1)을 클릭한 후 [Run]을 실행하게 되면 테스트 클래스의 전체 테스트 케이스가 실행 됩니다.
(2)에서 [Run]을 실행한다면 해당 테스트 케이스만 실행할 수 있습니다.
(2)에서 [Run]을 실행시켜 assertionTest() 테스트 케이스를 실행해 보겠습니다.

테스트 케이스 실행 결과 중에서 (2) 박스 와 같이 테스트에 통과(passed) 한 결과를 보기 위해서는 (1)의 체크 박스에
체크가 되어 있어야 합니다.
(2)를 보면 assertionTest1() 메서드에 추가한 @DisplayName("Hello JUnit Test")의 값이 실행 결과에 표시되는 걸 확인할 수 있습니다. 테스트 케이스가 성공(passed )이면 테스트 결과에 초록색 체크 아이콘이 표시됩니다.
이번엔 expected 변수의 값을 “Hello, World”로 바꾸겠습니다.

화면을 보면 (1)을 통해서 테스트 실행 결과가 실패(failed) 임을 알 수 있고, (2)를 통해서 왜 실패했는지에 대한 설명을
볼 수 있습니다.
기대했던 값은 “Hello, World”인데, 실제 결과 값은 “Hello, JUnit”이다라고 설명하고 있습니다.
- assertNotNull() 메소드 사용 : Null 여부 테스트
import com.codestates.CryptoCurrency;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertNotNull;
public class AssertionNotNullTest {
@DisplayName("AssertionNull() Test")
@Test
public void assertNotNullTest() {
String currencyName = getCryptoCurrency("ETH");
// (1)
assertNotNull(currencyName, "should be not null");
}
private String getCryptoCurrency(String unit) {
return CryptoCurrency.map.get(unit);
}
}
assertNotNull() 메서드를 사용하면 테스트 대상 객체가 null 이 아닌지를 테스트할 수 있습니다.
assertNotNull() 메서드의 첫 번째 파라미터는 테스트 대상 객체이고, 두 번째 파라미터는 테스트에 실패했을 때,
표시할 메시지입니다.
import java.util.HashMap;
import java.util.Map;
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");
}
}
실행 결과는 “ETH”에 해당하는 암호 화폐 이름이 map에 저장이 되어 있기 때문에 “passed”입니다.
만약 실패했다면, 아래와 같은 결과가 나왔을 겁니다.

- assertThrows() 메소드 사용 : 예외(Exception) 테스트
import com.codestates.CryptoCurrency;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class AssertionExceptionTest {
@DisplayName("throws NullPointerException when map.get()")
@Test
public void assertionThrowExceptionTest() {
// (1)
assertThrows(NullPointerException.class, () -> getCryptoCurrency("XRP"));
}
private String getCryptoCurrency(String unit) {
return CryptoCurrency.map.get(unit).toUpperCase();
}
}
이번에는 assertThrows()를 사용해서 호출한 메서드의 동작 과정 중에 예외가 발생하는지 테스트해보겠습니다.
getCryptoCurrency() 메서드를 호출했을 때, NullPointerException이 발생하는지 테스트하고 있습니다.
(1)에서 assertThrows()의 첫 번째 파라미터에는 발생이 기대되는 예외 클래스를 입력하고, 두 번째 파라미터인 람다 표현식에서는 테스트 대상 메서드를 호출하면 됩니다.
테스트 케이스를 실행하면 getCryptoCurrency() 메서드가 호출되고, 파라미터로 전달한 “XRP”라는 키에 해당하는 암호 화폐가 있는지 map에서 찾습니다.
XRP에 해당하는 암호 화폐는 map에 존재하지 않기 때문에 map에서 반환된 값은 null이 될 것입니다.
그리고 map에서 반환된 값이 null인 상태에서 toUpperCase()를 호출해서 대문자로 변환하려고 했기 때문에 NullPointerException이 발생할 것입니다.
따라서 (1)에서 NullPointerException이 발생할 것이라고 기대했기 때문에 테스트 실행 결과는 “passed”입니다.
이 때, 예외 타입이 다를 경우 실행 결과로 “failed”을 반환합니다.
예를 들어 NullPointerException.class 대신에 IllegalStateException.class으로 입력 값을 넣는다면 "failed"를 반환합니다.
여기서 만약 NullPointerException.class 대신에 RuntimeException.class 또는 Exception.class으로 입력 값을 넣는다면
"passed"를 반환합니다.
그 이유는 NullPointerException은 RuntimeException을 상속하는 하위 타입이고, RuntimeException은 Exception을
상속하는 하위 타입이기 때문입니다.
✔ 테스트 케이스 실행 전, 전처리
JUnit에서 테스트 케이스를 실행하기 전에 어떤 객체나 값에 대한 초기화 작업 등의 전처리 과정을 사용할 수 있는 애너테이션으로 @BeforeEach와 @BeforeAll()이 있습니다.
- @BeforeEach
@BeforeEach 애너테이션을 추가한 메서드는 테스트 케이스가 각각 실행될 때마다 테스트 케이스 실행 직전에 먼저 실행되어 초기화 작업 등을 진행할 수 있습니다.
- @BeforeAll()
@BeforeAll()은 @BeforeEach()와 달리 클래스 레벨에서 테스트 케이스를 한꺼번에 실행시키면 테스트 케이스가 실행되기 전에 딱 한 번만 초기화 작업을 할 수 있도록 해주는 애너테이션입니다.
@BeforeAll 애너테이션을 추가한 메서드는 정적 메서드(static method)여야 합니다.
✔ 테스트 케이스 실행 후, 후처리
JUnit에서는 테스트 케이스 실행이 끝난 시점에 후처리 작업을 할 수 있는 @AfterEach, @AfterAll 같은 애너테이션도 지원합니다.
이 애너테이션은 @BeforeEach , @BeforeAll과 동작 방식은 같고, 호출되는 시점만 반대입니다.
✔ Assumption을 이용한 조건부 테스트
Assumption은 ‘~라고 가정하고’라는 표현을 쓸 때의 ‘가정’에 해당합니다.
JUnit 5의 Assumption 기능을 사용하면 특정 환경에만 테스트 케이스가 실행되도록 할 수 있습니다.
public class AssumptionTest {
@DisplayName("Assumption Test")
@Test
public void assumptionTest() {
// (1)
assumeTrue(System.getProperty("os.name").startsWith("Windows"));
// assumeTrue(System.getProperty("os.name").startsWith("Linux")); // (2)
System.out.println("execute?");
assertTrue(processOnlyWindowsTask());
}
private boolean processOnlyWindowsTask() {
return true;
}
}
위 예시 코드의 (1)에서 assumeTrue() 메서드는 파라미터로 입력된 값이 true이면 나머지 아래 로직들을 실행합니다.
만약, 테스트 케이스를 실행하는 PC의 운영체제(OS)가 윈도우(Windows)라면 assumeTrue() 메서드의 파라미터 값이 true가 될 것이므로 assumeTrue() 아래 나머지 로직들이 실행이 될 것이고, 여러분들의 PC 운영체제(OS)가 윈도우(Windows)가 아니라면 assumeTrue() 아래 나머지 로직들이 실행되지 않을 것입니다.
assumeTrue()는 특정 OS 환경 등의 특정 조건에서 선택적인 테스트가 필요하다면 유용하게 사용할 수 있는 JUnit 5의 API입니다.