Test ‐ JUnit5 - dnwls16071/Backend_Summary GitHub Wiki

📚 JUnit5

스크린샷 2025-06-22 16 13 14

  • Platform : 테스트를 실행시켜주는 런처를 제공, TestEngine API 제공
  • Jupiter : TestEngine API 구현체로 JUnit5를 제공
  • Vintage : JUnit4와 JUnit3을 지원하는 TestEngine 구현체

지원되는 어노테이션 정리

  • @BeforeAll : Denotes that the annotated method should be executed before all @Test
  • @AfterAll : Denotes that the annotated method should be executed after all @Test
  • @BeforeEach : Denotes that the annotated method should be executed before each @Test
  • @AfterEach : Denotes that the annotated method should be executed after each @Test
  • @DisplayNameGeneration : Declares a custom display name generator for the test class. Such annotations are inherited.
  • @DisplayName : Declares a custom display name for the test class or test method. Such annotations are not inherited.
  • assertNotNull : 값이 null이 아닌지를 확인
  • assertEquals : 실제 값이 기대한 값과 같은지를 확인
  • assertTrue : 다음 조건이 true인지 확인
  • assertAll : 모든 확인 구문 확인
  • assertThrows : 지정한 예외가 발생했는지를 확인
  • assertTimeout : 특정 시간 안에 실행이 완료되는지를 확인

❗회사에서 개발하면서 테스트 코드를 작성하는 것도 좋을 것 같다. 기회가 된다면 데드라인을 넘기지 않는 선에서 작성하는 버릇을 들여야겠다.

package com.example.test;

import com.example.test.domain.Study;
import org.junit.jupiter.api.*;

import static org.assertj.core.api.Assertions.assertThat;

@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
class StudyTest {

    @Test
    @DisplayName("스터디 만들기 ╯°□°)╯")
    void create_new_study() {
        Study actual = new Study(10);
        assertThat(actual.getLimit()).isGreaterThan(0);
    }

    @Test
    @DisplayName("스터디 만들기 \uD83D\uDE31")
    void create_new_study_again() {
        System.out.println("create1");
    }

    @BeforeAll
    static void beforeAll() {
        System.out.println("before all");
    }

    @AfterAll
    static void afterAll() {
        System.out.println("after all");
    }

    @BeforeEach
    void beforeEach() {
        System.out.println("Before each");
    }

    @AfterEach
    void afterEach() {
        System.out.println("After each");
    }

}

📚 JUnit5 조건에 따라 테스트 실행하기

  • assumeTrue : 특정 조건을 만족하는 경우에 테스트를 실행하는 방법
  • assumeThat : 특정 조건을 만족하는 경우에 테스트를 실행하는 방법
  • @Enable__ & @Disabled__ : OnOS, OnJre, IfSystemProperty, IfEnvironmentVariable, If

📚 JUnit5 태깅과 필터링

  • @Tag : 테스트 메서드에 태그를 추가해 테스트 그룹을 만들어 원하는 테스트 그룹만을 실행할 수 있는 방법

스크린샷 2025-06-22 18 21 48

📚 JUnit5 커스텀 태그

  • JUnit5 어노테이션을 조합해 커스텀 태그를 만들 수 있다.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Test
@Tag("fast")
public @interface CustomAnnotation {
}

📚 JUnit5 테스트 반복

  • @RepeatedTest : Denotes that a method is a test template for a repeated test. Such methods are inherited unless they are overridden.
  • @ParameterizedTest : Denotes that a method is a parameterized test. Such methods are inherited unless they are overridden.
@DisplayName("스터디 만들기")
@RepeatedTest(value = 10, name = "{displayName} {currentRepetition}/{totalRepetitions}")
void repeatTest(RepetitionInfo repetitionInfo) {
    System.out.println("test : " + repetitionInfo.getCurrentRepetition() + " / " + repetitionInfo.getTotalRepetitions());
}

@ParameterizedTest(name = "{index} {displayName} message={0}")
@ValueSource(strings = {"날씨가", "많이", "추워지고", "있네요"})
void parameterizedTest(String message) {
    System.out.println("message = " + message);
}
  • @ValueSource : @ValueSource is one of the simplest possible sources. It lets you specify a single array of literal values and can only be used for providing a single argument per parameterized test invocation.
  • @NullSource : provides a single null argument to the annotated @ParameterizedClass or @ParameterizedTest
  • @EmptySource : provides a single empty argument to the annotated @ParameterizedClass or @ParameterizedTest for parameters
  • @NullAndEmptySource : a composed annotation that combines the functionality of @NullSource and @EmptySource
  • @EnumSource : @EnumSource provides a convenient way to use Enum constants
  • @MethodSource : @MethodSource allows you to refer to one or more factory methods of the test class or external classes
  • @CsvSource : @CsvSource allows you to express argument lists as comma-separated values
  • @CsvFileSource : @CsvFileSource lets you use comma-separated value (CSV) files from the classpath or the local file system
  • @ArgumentSource : @ArgumentsSource can be used to specify a custom, reusable ArgumentsProvider, Note that an implementation of ArgumentsProvider must be declared as either a top-level class or as a static nested class
@ParameterizedTest(name = "{index} {displayName} message={0}")
@ValueSource(ints = {10, 20, 40})
void parameterizedTest(@ConvertWith(StudyConverter.class) Study study) {
    System.out.println("study = " + study.getLimit());
}

static class StudyConverter extends SimpleArgumentConverter {

    @Override
    protected Object convert(Object source, Class<?> targetType) throws ArgumentConversionException {
        assertEquals(Study.class, targetType, "Can only convert to Study");
        return new Study(Integer.parseInt(source.toString()));
    }
}

📚 JUnit5 테스트 인스턴스

  • JUnit은 테스트 메서드마다 테스트 인스턴스를 새로 만든다.
  • 테스트 메서드를 독립적으로 실행해 예상치 못한 부작용을 막기 위함이다. 이런 기본 전략을 JUnit5에서 변경할 수 있다.
  • @TestInstance(LifeCycle.PER_CLASS)
    • 테스트 클래스당 인스턴스를 하나만 만들어 사용한다.
    • 필요에 따라 테스트 간 공유하는 모든 상태를 @BeforeEach 또는 @AfterEach에서 초기화 할 필요가 있다.
    • 테스트 A가 테스트 B의 실행에 영향을 미치면 안되기 때문에 이런 것과 무관한 경우의 테스트라면 한 번쯤은 고민해 볼 필요가 있어보인다.
@TestInstance(value = TestInstance.Lifecycle.PER_CLASS)

📚 JUnit5 테스트 순서

  • 경우에 따라 특정 순서대로 테스트를 실행하고픈 순간이 온다. 이런 경우에는 테스트 메서드를 원하는 순서에 따라 실행하도록 @TestInstance(LifeCycle.PER_CLASS)와 함께 @TestMethodOrder를 사용할 수 있다.
  • MethodOrderer 구현체를 설정한다. -> 기본 구현체 : Alphanumeric, OrderAnnotation, Random
  • @TestInstance : Used to configure the test instance lifecycle for the annotated test class. Such annotations are inherited.
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
@TestInstance(value = TestInstance.Lifecycle.PER_CLASS)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class) // 구현체 적용
class StudyTest {

    @Test
    @CustomAnnotation
    @Order(1) // 순서 지정
    @DisplayName("스터디 만들기 ╯°□°)╯")
    void create_new_study() {
        String test_env = System.getenv("TEST_ENV");
        System.out.println("test_env = " + test_env);
        assumeTrue("LOCAL".equalsIgnoreCase(test_env));
        assumeThat(test_env).isEqualToIgnoringCase("LOCAL");

        Study study = new Study(10);
        assertThat(study.getLimit()).isGreaterThan(0);
    }

    @Test
    @CustomAnnotation
    @Order(2) // 순서 지정
    @DisplayName("스터디 만들기 \uD83D\uDE31")
    void create_new_study_again() {
        System.out.println("create1");
    }

}

📚 JUnit5 - junit-platform.properties

  • JUnit5 설정 파일로 클래스패스 루트(/src/test/resources/)에 넣어두면 적용이 된다.
  • 테스트 인스턴스 라이프사이클 설정 : junit.jupiter.testinstance.lifecycle.default = per_class
  • 확장팩 자동 감지 기능 설정 : junit.jupiter.extensions.autodetection.enabled = true
  • @Disabled 무시하고 진행하는 설정 : junit.jupiter.conditions.deactivate = org.junit.*DisabledCondition
  • 테스트 이름 표기 전략 설정 : junit.jupiter.displayname.generator.default = \org.junit.jupiter.api.DisplayNameGenerator$ReplaceUnderscores

📚 JUnit5 확장 모델

  • JUnit4 확장 모델은 @RunWith(Runner), TestRule, MethodRule
  • JUnit5 확장 모델은 Extension
  • 확장팩을 등록하는 방법
    • 선언적 등록 방법 : @ExtendWith
    • 프로그래밍 등록 방법 : @RegisterExtension