백기선 님의 인프런 강의 "더 자바, 애플리케이션을 테스트하는 다양한 방법"를 정리한 글입니다.
1. JUnit 5
- 자바 개발자들이 가장 많이 사용하는 테스트 프레임워크. (93%의 자바 개발자가 JUnit 사용)
구조
구성 요소 | 설명 |
Platform |
테스트 실행을 위한 런처 및 TestEngine API 제공
|
Jupiter |
JUnit 5의 주요 TestEngine 구현체
|
Vintage |
JUnit 4 및 3을 지원하는 TestEngine 구현체
|
시작하기
plugins {
id 'java'
}
group 'com.example'
version '1.0-SNAPSHOT'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
}
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0'
}
test {
useJUnitPlatform()
}
- 버전 요구: Java 8 이상
- 스프링 부트 프로젝트에서는 JUnit 5 의존성이 기본적으로 포함됨.
JUnit 5 확장 모델
- JUnit 4에서 @RunWith/@Rule을 대체하는 Extension
확장팩 등록 방법
등록 방법 | 설명 | 사용 위치 |
선언적 등록 | @ExtendWith 애노테이션 테스트 클래스 또는 메서드에 명시적으로 등록 |
클래스 / 메서드 |
프로그래밍적 등록 | @RegisterExtension 필드를 통해 런타임에 확장 등록 | 필드 (static/non-static) |
자동 등록 | ServiceLoader 기반으로 META-INF/services에 확장 클래스 등록 | 클래스패스 |
예제) 선언적 등록
더보기
@ExtendWith(MySimpleExtension.class)
public class ExtendWithExampleTest {
@Test
void testSomething() {
System.out.println("Test executed");
}
}
// 간단한 확장 클래스
class MySimpleExtension implements BeforeEachCallback {
@Override
public void beforeEach(ExtensionContext context) {
System.out.println("BeforeEach from MySimpleExtension");
}
}
예제) 프로그래밍적 등록
더보기
public class RegisterExtensionExampleTest {
@RegisterExtension
static MySimpleExtension extension = new MySimpleExtension();
@Test
void testWithRegisterExtension() {
System.out.println("Test executed");
}
}
class MySimpleExtension implements BeforeEachCallback {
@Override
public void beforeEach(ExtensionContext context) {
System.out.println("BeforeEach from MySimpleExtension");
}
}
예제) 자동 등록
더보기
public class AutoExtension implements BeforeEachCallback {
@Override
public void beforeEach(ExtensionContext context) {
System.out.println("BeforeEach from AutoExtension (ServiceLoader)");
}
}
src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension
AutoExtension
public class ServiceLoaderExtensionTest {
@Test
void testWithServiceLoader() {
System.out.println("Test executed");
}
}
JUnit 4와 JUnit 5 비교
항목 | JUnit 4 | JUnit 5 |
태그 | @Category(Class) | @Tag(String) |
확장 모델 | @RunWith, @Rule |
@ExtendWith, @RegisterExtension
|
테스트 전/후 | @Before, @After |
@BeforeEach, @AfterEach
|
테스트 전체 전/후 | @BeforeClass, @AfterClass |
@BeforeAll, @AfterAll
|
JUnit 4 마이그레이션
- JUnit 4 테스트 실행: junit-vintage-engine 의존성 추가
- @Rule 관련: @EnableRuleMigrationSupport 사용하여 ExternalResource, Verifier, ExpectedException 지원
SpringBoot 설정 파일 (junit-platform.properties)
junit.jupiter.testinstance.lifecycle.default = per_class
junit.jupiter.extensions.autodetection.enabled = true
2. 애노테이션
기본 애노테이션
애노테이션 | 설명 |
@Test | 테스트 메서드 지정 |
@BeforeAll | 모든 테스트 전에 한 번 실행 (static 필요) |
@AfterAll | 모든 테스트 후에 한 번 실행 (static 필요) |
@BeforeEach | 각 테스트 전에 실행 |
@AfterEach | 각 테스트 후에 실행 |
@Disabled |
테스트 실행하지 않음
|
예제
더보기
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class ExampleTest {
@BeforeAll
static void setUpBeforeClass() {
System.out.println("🔧 Before All Tests");
}
@AfterAll
static void tearDownAfterClass() {
System.out.println("🧹 After All Tests");
}
@BeforeEach
void setUp() {
System.out.println("➡️ Before Each Test");
}
@AfterEach
void tearDown() {
System.out.println("⬅️ After Each Test");
}
@Test
void additionTest() {
System.out.println("✅ Running addition test");
assertEquals(2, 1 + 1);
}
@Test
@Disabled("준비 중인 테스트입니다")
void disabledTest() {
// 이 테스트는 실행되지 않습니다.
}
}
테스트 이름 표시
애노테이션 | 설명 |
@DisplayName |
개별 테스트 메서드나 클래스에 표시될 사용자 지정 이름 설정
|
@DisplayNameGeneration |
테스트 이름 표기 방식 설정
(예: ReplaceUnderscores, IndicativeSentences) |
예제) @DisplayNameGeneration
더보기


1. ReplaceUnderscores
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
public class DisplayNameTest {
@Test
void login_should_succeed_with_correct_credentials() {
assertTrue(true);
}
@Test
@DisplayName("❌ 비밀번호가 틀리면 로그인에 실패해야 한다")
void login_should_succeed_with_wrong_password() {
assertTrue(true);
}
}

2. IndicativeSentences
@DisplayNameGeneration(DisplayNameGenerator.IndicativeSentences.class)
public class DisplayNameTest {
@Test
void login_should_succeed_with_correct_credentials() {
assertTrue(true);
}
@Test
@DisplayName("❌ 비밀번호가 틀리면 로그인에 실패해야 한다")
void login_should_succeed_with_wrong_password() {
assertTrue(true);
}
}

3. Assertions
- Assertions API: org.junit.jupiter.api.Assertions 사용
메서드 | 설명 |
assertEquals(expected, actual) |
두 값이 같은지 비교
|
assertNotNull(actual) |
값이 null이 아님을 확인
|
assertTrue(condition) |
주어진 조건이 true인지 확인
|
assertAll(executables...) |
여러 조건을 묶어서 동시에 검증
|
assertThrows(expectedType, executable) |
특정 예외가 발생하는지 확인
|
assertTimeout(duration, executable) |
주어진 시간 내에 실행 완료되는지 확인
|
예제
더보기
public class AssertionTest {
@Test
void testAssertEquals() {
int actual = 2 + 3;
int expected = 5;
assertEquals(actual, expected);
}
@Test
void testAssertNotNull() {
assertNotNull("Junit");
}
@Test
void testAssertTrue() {
assertTrue(20 > 10);
}
@Test
void testAssertAll() {
String name = "JUnit";
int age = 10;
assertAll("grouped assertions",
() -> assertNotNull(name),
() -> assertTrue(age > 5),
() -> assertEquals(name, "JUnit")
);
}
@Test
void testAssertThrows() {
assertThrows(IllegalArgumentException.class, () -> {
throw new IllegalArgumentException("Invalid arguments");
});
}
@Test
void testAssertTimeout() {
assertTimeout(Duration.ofSeconds(2), () -> {
Thread.sleep(1000);
});
}
}
4. Assumptions
- 특정 조건에서만 테스트 실행
항목 | 설명 |
assumeTrue(condition) |
조건이 true일 때만 테스트 실행
|
assumeFalse(condition) |
조건이 false일 때만 테스트 실행
|
assumingThat(condition, executable) |
조건이 true일 때만 해당 블록 실행, 나머지는 실행됨
|
@EnabledOnOs(OS) |
지정된 운영체제에서만 테스트 실행
|
@DisabledOnOs(OS) |
지정된 운영체제에서는 테스트 실행 안 함
|
@EnabledIfSystemProperty(...) |
시스템 속성이 지정된 값일 때만 테스트 실행
|
@EnabledIfEnvironmentVariable(...) |
환경변수가 지정된 값일 때만 테스트 실행 (VM Options)
|
예제
더보기
public class AssumptionTest {
@Test
void testAssumeTrue() {
assumeTrue(1+1==2, "조건이 true일 때만 실행됨");
}
@Test
void testAssumeFalse() {
assumeFalse(1+1==3, "조건이 false일 때만 실행됨");
}
@Test
void testAssumingThat() {
String env = "dev";
assumingThat("dev".equals(env), () -> System.out.println("dev 환경에서만 실행됨"));
}
@Test
@EnabledOnOs(OS.MAC)
void onlyOnMac() {
System.out.println("Mac OS 환경에서만 실행됨");
}
@Test
@DisabledOnOs(OS.WINDOWS)
void notOnlyWindows() {
System.out.println("Window OS 환경에서는 실행되지 않음");
}
@Test
@EnabledIfSystemProperty(named = "user.country", matches="KR")
void onlyIfSystemPropertyMatches() {
System.out.println("user.country 시스템 속성이 'KR' 일 때만 실행됨");
}
@Test
@EnabledIfEnvironmentVariable(named = "ENV", matches="staging")
void onlyIfEnvVarMatches() {
System.out.println("ENV 환경변수가 'staging'일 때만 실행됨");
}
}
5. 태깅 및 필터링
@Tag
- 실행 시 해당 태그를 기준으로 필터링 가능
- 테스트 메서드 or 클래스에 추가
예제
더보기
public class TagTest {
@Test
@Tag("fast")
void fastTest() {
System.out.println("빠른 테스트 실행");
}
@Test
@Tag("slow")
void slowTest() {
System.out.println("느린 테스트 실행");
}
}
test {
useJUnitPlatform {
includeTags 'fast'
excludeTags 'slow'
}
}
./gradlew test
6. 커스텀 태그
- 애노테이션을 조합하여 커스텀 태그 생성
예시
더보기
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Tag("fast")
@Test
public @interface FastTest { }
7. 테스트 반복하기
애노테이션 | 설명 |
@RepeatedTest(int) |
지정한 횟수만큼 테스트를 반복 실행
|
@ParameterizedTest |
매개변수를 사용하여 여러 값으로 반복 테스트
|
- @ValueSource |
단일 값 목록을 소스로 제공
|
- @CsvSource |
CSV 형식으로 여러 인자 제공
|
- @MethodSource |
메서드로부터 인자 목록을 받아 테스트
|
예시
더보기
public class RepeatedAndParameterizedTest {
@RepeatedTest(value = 3, name = "반복 테스트 {currentRepetition} / {totalRepetitions}")
void repeatedTest(RepetitionInfo repetitionInfo) {
// System.out.println("실행 번호: " + repetitionInfo.getCurrentRepetition());
}
@ParameterizedTest
@ValueSource(strings = {"apple", "banana", "cherry"})
void testWithValueSource(String fruit) {
System.out.println("과일: " + fruit);
assertNotNull(fruit);
}
@ParameterizedTest
@CsvSource({
"apple, 5",
"banana, 3",
"cherry, 7"
})
void testWithCsvSource(String fruit, int cnt) {
System.out.println("과일: " + fruit + " 개수: " + cnt);
assertTrue(cnt > 0);
}
@ParameterizedTest
@MethodSource("provideStringsForTest")
void testWithMethodSource(String language, int version) {
System.out.println(language + " 버전: " + version);
}
static Stream<Arguments> provideStringsForTest() {
return Stream.of(
Arguments.of("java", 8),
Arguments.of("kotlin", 1),
Arguments.of("spring", 5)
);
}
}
8. 테스트 인스턴스
항목 | 설명 |
기본 동작 |
테스트 메소드마다 새로운 인스턴스를 생성
테스트 간 상태 공유 없음 |
@TestInstance(Lifecycle.PER_CLASS) |
테스트 클래스 전체에서 하나의 인스턴스를 사용하여 상태 공유 가능
테스트 간 상태 공유가 필요할 경우, @BeforeEach 또는 @AfterEach로 초기화 |
예시
더보기
public class LifecycleTest {
private int count = 0;
@Test
void test1() {
count++;
System.out.println("test1 count: " + count);
}
@Test
void test2() {
count++;
System.out.println("test2 count: " + count);
}
}
test1 count: 1
test2 count: 1
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class LifecyclePerClassTest {
private int count = 0;
@Test
void test1() {
count++;
System.out.println("test1 count: " + count);
}
@Test
void test2() {
count++;
System.out.println("test2 count: " + count);
}
}
test1 count: 1
test2 count: 2
9. 테스트 순서
@TestMethodOrder
- 테스트 메소드의 실행 순서를 정의할 때 사용
- MethodOrderer.OrderAnnotation
- MethodOrderer.Alphanumeric
- MethodOrderer.Random
- 커스텀
예시) MethodOrderer.OrderAnnotation
더보기
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class OrderedTest {
@Test
@Order(2)
void secondTest() {
System.out.println("second test");
}
@Test
@Order(1)
void firstTest() {
System.out.println("first test");
}
@Test
@Order(3)
void thirdTest() {
System.out.println("third test");
}
}
예시) Custom
더보기
public class CustomSpeedOrdered implements MethodOrderer {
@Override
public void orderMethods(MethodOrdererContext methodOrdererContext) {
methodOrdererContext.getMethodDescriptors().sort(Comparator.comparingInt(descriptor -> {
String name = descriptor.getMethod().getName();
if (name.endsWith("fast")) return 1;
else if (name.endsWith("medium")) return 2;
else if (name.endsWith("slow")) return 3;
return Integer.MAX_VALUE;
}));
}
}
@TestMethodOrder(CustomSpeedOrdered.class)
public class CustomOrderTest {
@Test
void test_medium() {
System.out.println("medium test");
}
@Test
void test_fast() {
System.out.println("fast test");
}
@Test
void test_slow() {
System.out.println("slow test");
}
}
출처
'Code > Test' 카테고리의 다른 글
[더 자바, 애플리케이션을 테스트하는 다양한 방법] 2. Mockito (0) | 2025.04.19 |
---|---|
[단위 테스트] 9. 목 처리에 대한 모범 사례 (0) | 2025.03.01 |
[단위 테스트] 8-2. 통합 테스트를 하는 이유: 인터페이스 (1) | 2025.03.01 |
[단위 테스트] 8-1. 통합 테스트를 하는 이유: 통합 테스트 (0) | 2025.02.28 |
[단위 테스트] 7-2. 가치 있는 단위 테스트를 위한 리팩터링: 감사 시스템 (0) | 2025.02.28 |