티스토리 뷰
자바프로그램 테스트를 위해서 주로 JUnit을 사용합니다. Spockframework도 있는데 각각의 장단점이 있는 것 같습니다.
JUnit5와 Spockframework에 관해서 정리합니다.
JUnit5란?
3개의 하위 모듈의 서브 프로젝트로 구성됨
JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage
JUnit Platform : 테스트 엔진, Console Launcher, JUni4 기반 러너 등 테스트 프레임워크를 시작하기 위한 기초적인 것을 제공한다.
JUnit Jupiter : JUnit5를 위한 테스트 엔진 및 API를 제공한다.
JUnit Vintage : JUnit3, 4를 위한 테스트 엔진을 제공한다. 하위 호환성을 위해 존재하는 프로젝트.
Migrating from JUnit4
Annotations reside in the
org.junit.jupiter.api
package. (JUni5 기본 패키지, 하위 버전과 패키지가 다름)Assertions reside in
org.junit.jupiter.api.Assertions
.Assumptions reside in
org.junit.jupiter.api.Assumptions
.@Before
and@After
no longer exist; use@BeforeEach
and@AfterEach
instead.@BeforeClass
and@AfterClass
no longer exist; use@BeforeAll
and@AfterAll
instead.@Ignore
no longer exists: use@Disabled
instead.@Category
no longer exists; use@Tag
instead.@RunWith
no longer exists; superseded by@ExtendWith
.@Rule
and@ClassRule
no longer exist; superseded by@ExtendWith
; see the following section for partial rule support.
Features | JUnit 5 | JUnit 4 |
Declares a test method | @Test | @Test |
Denotes that the annotated method will be executed before all test methods in the current class | @BeforeAll | @BeforeClass |
Denotes that the annotated method will be executed after all test methods in the current class | @AfterAll | @AfterClass |
Denotes that the annotated method will be executed before each test method | @BeforeEach | @Before |
Denotes that the annotated method will be executed after each test method | @AfterEach | @After |
Disable a test method or a test class | @Disable | @Ignore |
Denotes a method is a test factory for dynamic tests in JUnit 5 | @TestFactory | N/A |
Denotes that the annotated class is a nested, non-static test class | @Nested | N/A |
Declare tags for filtering tests | @Tag | @Category |
Register custom extensions in JUnit 5 | @ExtendWith | N/A |
Repeated Tests in JUnit 5 | @RepeatedTest | N/A |
@DisplayName을 사용하면 띄어쓰기, 특수문자, 이모지도 허용되어서 좀 더 알기 쉽게 테스트 제목을 작성할 수 있습니다.
class JUnit5Test {
@DisplayName("해당 글자가 컬렉션에 포함되어 있는지 확인 👏")
@Test
void testOwners() {
List<String> owners = Arrays.asList("예파파", "예마마");
assertAll(
() -> assertTrue(owners.contains("예파파"), "예파파 포함됨"),
() -> assertTrue(owners.contains("예마마1"), "예마마 포함됨")
);
}
}
JUnit4에서 @Test(expected=..... 를 사용하여 Exception을 확인하였지만 message까지 확인하려면 @Rule 애노테이션을 달은 ExpectedException을 만들고 테스트 내에서 expect, expectMessage 메서드를 통해 확인해야 했습니다.
JUnit4에서는 아래와 같이 처리합니다.
@Test
void exceptionTesting() {
Throwable exception = assertThrows(IllegalArgumentException.class, () -> {
throw new IllegalArgumentException("a message");
});
assertEquals("a message", exception.getMessage());
}
@Tag를 통해서 필터링 후에 테스트를 실행할 수 있습니다.
@Tag("integration"), @Tag("slow") 등등 프로젝트에 맞는 테스트를 만들고 사용자 정의 애노테이션으로 만들어서 사용하기도 합니다.
예를 들면 @Tag("integration")을 사용해서 @IntegrationTest, @CITest 란 애노테이션을 정의합니다.
@TestFactory를 통해서 동적 테스트를 만들 수 있고요
다음과 같이 @Nested 애노테이션을 통해서 테스트 그룹을 더 잘 표현할 수 있게 만들 수 있습니다.
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.EmptyStackException;
import java.util.Stack;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
@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());
}
}
}
}
Spock Testing and Specification Framework
쉬운 Stubbing
subscriber.receive("message1") >> "ok"
subscriber.receive("message2") >> "fail"
Data Driven Testing
동일한 테스트를 다양한 입력과 예상 결과값으로 테스트 하는 것이 좋을 때가 있는데 Spock이 이 부분에 장점이 있습니다.
아래와 같은 테스트가 있을 때
class MathSpec extends Specification {
def "maximum of two numbers"() {
expect:
// exercise math method for a few different inputs
Math.max(1, 3) == 3
Math.max(7, 4) == 7
Math.max(0, 0) == 0
}
}
Data table을 이용해서 처리합니다.
class MathSpec extends Specification {
def "maximum of two numbers"(int a, int b, int c) {
expect:
Math.max(a, b) == c
where:
a | b | c
1 | 3 | 3
7 | 4 | 7
0 | 0 | 0
}
}
Intellij 2018.2.2에서 method signiture를 변경했으나 인지를 못하네요
당연히 테스트를 돌려보면 알 수 있는 부분이긴 하지만 아쉽네요
참고
JUnit 5 가이드
https://junit.org/junit5/docs/current/user-guide
JUnit4, JUnit 5 비교
http://www.vogella.com/tutorials/JUnit/article.html
Spock Testing and Specification Framework
http://spockframework.org/spock/docs/1.1/all_in_one.html
https://www.baeldung.com/junit-assert-exception
https://howtodoinjava.com/junit5/expected-exception-example/
https://howtoprogram.xyz/2016/08/10/junit-5-vs-junit-4/
https://www.baeldung.com/junit-before-beforeclass-beforeeach-beforeall
https://blog.codefx.org/libraries/junit-5-basics/
https://blog.codefx.org/libraries/junit-5-setup/
http://blog.codeleak.pl/2017/11/junit-5-meets-assertj.html
intellij 2018.1 @Tag annotaion support
https://jaxenter.com/intellij-ide-public-preview-build-142006.html