JUnit5

zwundzwzig·2023년 11월 8일
0

Test

목록 보기
1/2
post-custom-banner

자바부터 다시 하자.
백기선님의 자바 스터디 2020 파트 중 JUnit 5

무심코 쓰던 테스트 프레임워크를 뜯어보고 아키텍쳐적으로 바라보고자 작성했다.

JUnit

JUnit은 Java 생태계에서 가장 널리 사용되는 단위 테스트 프레임워크 중 하나이고, 5버전부터 jdk1.8을 지원한다고 한다.

JUnit은 세 가지 모듈로 구성돼 우리의 단위 테스트를 도와준다.
  • JUnit Platform
    • JVM에서 테스트 프레임워크를 시작하는 역할
    • TestEngine API를 제공해 테스트 프레임워크 개발 가능한 환경 구축
  • Jupiter
    • JUnit 5에서 테스트 작성을 위한 새로운 프로그래밍 및 확장 모델
    • TestEngine API 구현체
  • Vintage
    • 과거 버전인 JUnit3 및 JUnit4의 구현 지원

Annotation

어노테이션설명
@Test테스트 메서드 위에 선언. 동명의 어노테이션이 4버전에도 존재.
@ParameterizedTest메서드가 매개 변수화된 테스트 임을 나타냄
@RepeatedTest메서드가 반복 테스트 를 위한 테스트 템플릿임을 나타냄
@TestFactory메서드가 동적 테스트를 위한 테스트 팩토리임을 나타냄
@TestTemplate테스트 환경에서 여러 번 호출되도록 설계된 테스트 사례용 템플릿임을 명시
@TestMethodOrder테스트 클래스의 테스트 메서드 실행 순서
@TestInstance테스트 클래스의 생명 주기를 설정
@DisplayName인자로 받은 문자열로 해당 메서드를 설명할 수 있는 기능
@DisplayName인자로 받은 문자열로 해당 메서드를 설명할 수 있는 기능
@BeforeEach, @AfterEach각각 클래스 내 각 메서드의 실행 전/후마다 실행되는 메서드. 라이프 사이클 메소드
@BeforeAll, @AfterAll각각 클래스 전체의 메서드 시작 전/후로 한번만 실행되는 메서드. 라이프 사이클 메소드
@Nested클래스가 테스트용 inner 클래스임을 명시. 테스트 클래스는 abstract이면 안됨
@Tag테스트 필터링을 위한 태그 선언에 사용
@Disabled테스트 클래스나 테스트 메서드를 비활성화하는 데 사용
@Timeout지정된 시간을 초과하는 테스트의 경우 실패시키는 데 사용
@ExtendWith단위 테스트에 공통적으로 사용할 확장 기능 선언하는 역할 stackoverflow
// 1) ParameterizedTest 예시
@ParameterizedTest
@ValueSource(strings = { "racecar", "radar", "able was I ere I saw elba" })
void palindromes(String candidate) {
    assertTrue(StringUtils.isPalindrome(candidate));
}
  
// 2) RepeatedTest 예시
@RepeatedTest(10)
void repeatedTest() {
    // ...
}
  
// 3) @TestMethodOrder 예시
@TestMethodOrder(OrderAnnotation.class)
class OrderedTestsDemo {

    @Test
    @Order(1)
    void nullValues() {
        // perform assertions against null values
    }

    @Test
    @Order(2)
    void emptyValues() {
        // perform assertions against empty values
    }

    @Test
    @Order(3)
    void validValues() {
        // perform assertions against valid values
    }

}
  
4) @Nested 예시
@DisplayName("A stack")
class TestingAStackDemo {

    Stack<Object> stack;

    @Test
    @DisplayName("is instantiated with new Stack()")
    void isInstantiatedWithNew() {
        new Stack<>();
    }

    @Nested
    @DisplayName("when new")
    class WhenNew {

        @BeforeEach
        void createNewStack() {
            stack = new Stack<>();
        }

        @Test
        @DisplayName("is empty")
        void isEmpty() {
            assertTrue(stack.isEmpty());
        }

        @Test
        @DisplayName("throws EmptyStackException when popped")
        void throwsExceptionWhenPopped() {
            assertThrows(EmptyStackException.class, stack::pop);
        }

        @Test
        @DisplayName("throws EmptyStackException when peeked")
        void throwsExceptionWhenPeeked() {
            assertThrows(EmptyStackException.class, stack::peek);
        }

        @Nested
        @DisplayName("after pushing an element")
        class AfterPushing {

            String anElement = "an element";

            @BeforeEach
            void pushAnElement() {
                stack.push(anElement);
            }

            @Test
            @DisplayName("it is no longer empty")
            void isNotEmpty() {
                assertFalse(stack.isEmpty());
            }

            @Test
            @DisplayName("returns the element when popped and is empty")
            void returnElementWhenPopped() {
                assertEquals(anElement, stack.pop());
                assertTrue(stack.isEmpty());
            }

            @Test
            @DisplayName("returns the element when peeked but remains not empty")
            void returnElementWhenPeeked() {
                assertEquals(anElement, stack.peek());
                assertFalse(stack.isEmpty());
            }
        }
    }
}
  
5) @Tag 예시
@Tag("fast")
@Tag("model")
class TaggingDemo {

    @Test
    @Tag("taxes")
    void testingTaxCalculation() {
    }

}
6) @Disabled 예시
  
@Disabled("Disabled until bug #99 has been fixed")
class DisabledClassDemo {
    @Test
    void testWillBeSkipped() {
    }
}
  
7) @ExtendWith 예시

@ExtendWith(SpringExtension.class)
class TestServiceTest {
    @MockBean
    TestService service;

    @Test
    void test() {
        assertNotNull(service); // Test succeeds
    }
}

@ExtendWith(MockitoExtension.class)
@ExtendWith(SpringExtension.class)
class TestServiceTest {
    @MockBean
    TestService service;

    @Test
    void test() {
        assertNotNull(service); // Test succeeds
    }
}

Jupiter Assertions

JUnit 4에 있는 많은 메소드와 함께 제공되며 Java 8 람다와 함께 사용하기에 적합.
모든 JUnit Jupiter 어설션은 클래스 static의 메서드.

메서드
assertEquals(expected, actual)
assertNotEquals(expected, actual)
assertTrue(boolean condition)
assertFalse(boolean condition)
assertNull(Object actual)
assertNotNull(Object actual)
assertAll()
assertThrows(ArithmeticException.class() -> divide(100,0))
assertTimeout(ofMillis(10), () -> Thread.sleep(100));
assertTimeoutPreemptively(ofMillis(10), () -> new CountDownLatch(1).await());
fail()

Jupiter Assumptions

특정 OS나 profile에 사용

class AssumptionsDemo {

    private final Calculator calculator = new Calculator();

    @Test
    void testOnlyOnCiServer() {
        assumeTrue("CI".equals(System.getenv("ENV")));
        // remainder of test
    }

    @Test
    void testOnlyOnDeveloperWorkstation() {
        assumeTrue("DEV".equals(System.getenv("ENV")),
            () -> "Aborting test: not on developer workstation");
        // remainder of test
    }

    @Test
    void testInAllEnvironments() {
        assumingThat("CI".equals(System.getenv("ENV")),
            () -> {
                // perform these assertions only on the CI server
                assertEquals(2, calculator.divide(4, 2));
            });

        // perform these assertions in all environments
        assertEquals(42, calculator.multiply(6, 7));
    }

}

🧷 참조 교재

profile
개발이란?
post-custom-banner

0개의 댓글