자바부터 다시 하자.
백기선님의 자바 스터디 2020 파트 중 JUnit 5
무심코 쓰던 테스트 프레임워크를 뜯어보고 아키텍쳐적으로 바라보고자 작성했다.
JUnit은 Java 생태계에서 가장 널리 사용되는 단위 테스트 프레임워크 중 하나이고, 5버전부터 jdk1.8을 지원한다고 한다.
JUnit은 세 가지 모듈로 구성돼 우리의 단위 테스트를 도와준다.어노테이션 | 설명 |
---|---|
@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
}
}
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() |
특정 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));
}
}