2018년 초에 프로젝트를 진행하면서 JUnit5 관련 스터디 한 자료가 있어서
블로그로 옮겨본다.
JUnit5
JUnit은 Java 단위테스트 작성을 위한 표준 framework
기존 우리가 사용하던버전은 JUnit4로 새로나온 JUnit5를 알아 볼께요
차이점
- JUnit 4가 단일 jar였던 것에 비해, JUnit 5는 크게 JUnit Platform, JUnit Jupiter, JUnit Vintage 모듈로 구성되어 있다.
- JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage
- 테스트 작성자를 위한 API 모듈과 테스트 실행을 위한 API가 분리되어 있다.
- 예를 들어, JUnit Jupiter는 테스트 코드 작성에 필요한 junit-jupiter-api 모듈과 테스트 실행을 위한 junit-jupiter-engine 모듈로 분리되어 있다.
- 자바 8 또는 그 이상 버전을 요구한다.
JUnit Platform
JUnit 플랫폼은 JVM에서 테스트 프레임 워크를 시작하기위한 기초 역할을합니다.
또한 플랫폼에서 실행되는 테스트 프레임 워크를 개발하기위한 TestEngine API를 정의합니다.
또한 플랫폼은 명령 행에서 플랫폼을 실행하고 Gradle 및 Maven 용 플러그인을 빌드하는 Console Launcher를 제공하며
플랫폼에서 TestEngine을 실행하기위한 JUnit 4 기반 Runner도 제공합니다.
JUnit Jupiter
JUnit 5에서 테스트 및 확장 작성을위한 새로운 프로그래밍 모델과 확장 모델의 조합입니다.
Jupiter 하위 프로젝트는 플랫폼에서 Jupiter 기반 테스트를 실행하기위한 TestEngine을 제공합니다.
JUnit Vintage
플랫폼에서 JUnit 3 및 JUnit 4 기반 테스트를 실행하기위한 TestEngine을 제공합니다.
Maven Dependency
1 |
|
JUnit4 와 달라진점
assert
- JUnit4
- 이 코드에서 만약 첫 번째 assertEquals()가 실패하면 그 시점에서 테스트 실패하므로 두 번째 assertEquals()를 실행하지 않는다. 이 두 assertEquals()는 실제로는 하나의 score를 검증하는 것이므로 개념적으로 하나를 검증하는 것이다
1
2
3
4
5
6
7
void assertEach() {
Game game = new Game(123);
Score score = game.guess(134);
assertEquals(1, score.getStrikes());
assertEquals(1, score.getBalls());
}
- 이 코드에서 만약 첫 번째 assertEquals()가 실패하면 그 시점에서 테스트 실패하므로 두 번째 assertEquals()를 실행하지 않는다. 이 두 assertEquals()는 실제로는 하나의 score를 검증하는 것이므로 개념적으로 하나를 검증하는 것이다
- JUnit5
- assertAll()은 함수형인터페이스인 Executable 목록을 파라미터로 갖는다.(Executable 인터페이스는 파라미터가 없고 리턴 타입이 void인 execute() 메서드를 정의하고 있다.)
- 함수형 인터페이스이므로 위 코드와 같이 람다식을 사용해서 여러개의 검증을 목록으로 전덜할 수 있다.
- assertAll()의 특징은 목록으로 받은 모두 Executable을 실행한다는 점이다. 그리고 그 중에서 실패한 검증에 대해서만 리포트를 한다. 예를 들어, 위 코드에서 assertAll()로 전달한 모든 Executable이 검증에 실패했다고 하자. 이를 인텔리J에서 실행해보면 assertAll()에서 실패한 모든 검증 결과가 콘솔에 출력되는 것을 알 수 있다.
1
2
3
4
5
6
7
8
9
10public class AssertAllTest {
void assertAllSample() {
Game game = new Game(123);
Score score = game.guess(145);
assertAll(
() -> assertEquals(2, score.getStrikes()),
() -> assertEquals(1, score.getBalls())
);
}
assertThrows
JUnit4
1
2
3
4
5
6
7
8.class) (expected = RuntimeException
public void testIsGroupSeats_SeatsAreInvalidByCache() throws Exception {
// given
List<Integer> logicalSeatIds = Arrays.asList(4, 5, 6);
mockingForSeatsAreInvalidByCache(logicalSeatIds);
// when
service.isSameGroupSeats(logicalPlanId, logicalSeatIds);
}JUnit5
1
2
3
4
5
6
7
8
void simple() {
assertThrows(ArithmeticException.class, () -> divide(100, 0));
}
private int divide(int op1, int op2) {
return op1 / op2;
}
보조 Annotation @DisplayName @Disabled
- @DisplayName은 테스트 클래스나 메서드의 표시 이름을 지정한다
- @Disabled 는 테스트 실행 대상에서 제외한다.
Assumption
- 특정 상황에 따른 TestCode를 실행
- Assumptions.assumeTrue()는 인자로 전달받은 값이 true이면 이후 테스트를 진행하고, 그렇지 않으면 테스트를 생략한다
1
2
3
4
5
6
7
8
9
void runTest() {
String osName = System.getProperty("os.name");
assumingThat(
osName.startsWith("Linux"), // (1) 가정 boolean 또는 BooleanSupplier
() -> assertEquals(1, 2) // (2) 가정을 충족할 때 실행할 코드(Executable 타입)
);
assertEquals(1, 1); // (3)
}
Nested
- 중첩된 구조로 테스트를 구성할 수 있습니다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71"A stack") (
class TestingAStackDemo {
Stack<Object> stack;
"is instantiated with new Stack()") (
void isInstantiatedWithNew() {
new Stack<>();
}
"when new") (
class WhenNew {
void createNewStack() {
stack = new Stack<>();
}
"is empty") (
void isEmpty() {
assertTrue(stack.isEmpty());
}
"throws EmptyStackException when popped") (
void throwsExceptionWhenPopped() {
assertThrows(EmptyStackException.class, () -> stack.pop());
}
"throws EmptyStackException when peeked") (
void throwsExceptionWhenPeeked() {
assertThrows(EmptyStackException.class, () -> stack.peek());
}
"after pushing an element") (
class AfterPushing {
String anElement = "an element";
void pushAnElement() {
stack.push(anElement);
}
"it is no longer empty") (
void isNotEmpty() {
assertFalse(stack.isEmpty());
}
"returns the element when popped and is empty") (
void returnElementWhenPopped() {
assertEquals(anElement, stack.pop());
assertTrue(stack.isEmpty());
}
"returns the element when peeked but remains not empty") (
void returnElementWhenPeeked() {
assertEquals(anElement, stack.peek());
assertFalse(stack.isEmpty());
}
}
}
}
참고
http://junit.org/junit5/docs/current/user-guide/
http://javacan.tistory.com/entry/JUnit-5-Intro?category=454313