assertEqual
을 사용했으나 사실 aseertThat
을 사용하는 것이 더 직관적으로 읽기 쉽고, 타입 안정성을 가진다. 지난 번에 정리했던 글
- 0.1. 환경설정
- 0.2. 간단한 테스트
- 0.3. 여러 개의 Assertions, assertAll
- 0.4. Assumptions
- 0.5. 예외 체크
- 0.6. JUnit5 어노테이션
- 어노테이션1 : @BeforeEach 와@BeforeAll 의 차이
- 어노테이션2 : @TestInstance
- 어노테이션3 : @Disabled
- 어노테이션4 : @DisplayName
- 어노테이션5 : @ParameterizedTest
- 어노테이션6 : @Nested
compile 'org.junit.jupiter:junit-jupiter-api:5.7.2'
가 추가하라고 하는데 그것이 없어도 잘 작동된다. assertEquals(좌, 우)
좌항과 우항이 같은지 test public class ExampleTest {
@Test
void shouldShowSimpleAssertion() {
Assertions.assertEquals(1, 1);
Assertions.assertEquals(1, 2);
}
}
@Test
@DisplayName("Should check all items in the list")
void shouldCheckAllItemsInTheList() {
List<Integer> numbers = List.of(2, 3, 5, 7);
Assertions.assertEquals(2, numbers.get(0));
Assertions.assertEquals(3, numbers.get(1));
Assertions.assertEquals(5, numbers.get(2));
Assertions.assertEquals(7, numbers.get(3));
}
@Test
@DisplayName("Should check all items in the list")
void shouldCheckAllItemsInTheList() {
List<Integer> numbers = List.of(2, 3, 5, 7);
Assertions.assertEquals(2, numbers.get(0));
Assertions.assertEquals(3, numbers.get(3));
Assertions.assertEquals(5, numbers.get(2));
Assertions.assertEquals(7, numbers.get(3));
}
assertAll
이 있다. 도중에 하나가 실패해도 모든 assertion을 체크하기 때문이다. 람다식으로 표현해서 assertAll
로 묶을 수 있다. assertAll
을 활용한 테스트 @Test
@DisplayName("Should check all items in the list")
void shouldCheckAllItemsInByAssertAll() {
List<Integer> numbers = List.of(2, 3, 5, 7);
Assertions.assertAll(
() -> assertEquals(1, numbers.get(0)), //실패
() -> assertEquals(3, numbers.get(1)),
() -> assertEquals(1, numbers.get(2)), //실패
() -> assertEquals(7, numbers.get(3)));
}
assertEquals
가 fail되게 작성했을 때 이 모든 것을 잡아주는 것을 알 수 있다. 특정 assumptions(가정)이 true
일 때만 test를 하고 싶을 때 사용한다.
public class ExampleTest {
private final String currentDayOfWeek = LocalDate.now().getDayOfWeek().toString();
@Test
@DisplayName("Should only run the test if some criteria are met")
void shouldOnlyRunTheTestIfSomeCriteriaAreMet() {
Assumptions.assumeTrue(currentDayOfWeek.equals("WEDNESDAY"));
assertEquals(1, 1);
}
}
assumptions
이 true
이기 때문에 테스트가 잘 통과된다 assumptions
을 틀리게 작성 @Test
@DisplayName("Should only run the test if some criteria are met")
void shouldOnlyRunTheTestIfSomeCriteriaAreMet() {
Assumptions.assumeTrue(currentDayOfWeek.equals("TUESDAY"));
assertEquals(1, 1);
}
assertEquals
가 아예 실행되지 않는다 @DisplayName("Should throw exception")
@Test
void shouldThrowException() {
assertThrows(RuntimeException.class, () -> {
Integer.parseInt("hello");
});
}
assertThatThrownBy
사용 @Test
@DisplayName("잘못된 위치에 node를 추가하면 예외가 발생해야한다")
void addNodeAtWrongPosition() {
assertThatThrownBy(() -> {
linkedList.add(head, new ListNode(1), 5);
}).isInstanceOf(IndexOutOfBoundsException.class);
}
어노테이션 | 설명 | 자세한 설명 |
---|---|---|
@Test | 테스트메소드라는 것을 알려준다 | |
@ParameterizedTest | 파라미터라이즈 테스트라는 것을 알려준다. 매개변수 전달하여 테스트 | docs |
@RepeatedTest | 동일한 테스트를반복할 때 | docs baeldung |
@TestMethodOrder | 테스트 실행 순서 정하기. @TestInstance 를 사용할 때 순서 정할 때 사용, 순서를 정하는 방법에는 여러 옵션이 있다. | docs |
@TestInstance | 길어서 아래 설명 | docs |
@DisplayName | 테스트에 이름 붙이기 | |
@BeforeEach | 동일 클래스 내에서 각각의 @Test, @RepeatedTest, @ParameterizedTest, or @TestFactory 가 붙은 메소드 실행 전 실행 | |
@AfterEach | 동일 클래스 내에서 각각의 @Test, @RepeatedTest, @ParameterizedTest, or @TestFactory 가 붙은 메소드 실행 후 실행 | |
@BeforeAll | 모든 all @Test, @RepeatedTest, @ParameterizedTest, and @TestFactory 메소드 전에 한번만 실행 | |
@Nested | 테스트 묶어줄 때 사용 , @BeforeAll and @AfterAll는 @TestInstance 로 클래스 단위의 환경변수를 만들어주지 않는 한 사용 불가 | |
@Disabled | 이 테스트는 무시하세요 |
@BeforeEach
와@BeforeAll
의 차이.각각의 테스트 전에 한번씩 실행 된다는 것(@BeforeEach
)과 모든 테스트 전에 한 번씩 실행되는 결과(@BeforeAll
)의 차이를 보기 위해 코드를 작성했다.
@BeforeEach
@DisplayName("A stack")
public class BeforeEachTest {
Stack<Object> stack;
String anElement = "an element";
@BeforeEach
void createNewStack() {
stack = new Stack<>();
stack.push(anElement);
}
@Test
@DisplayName("it is no longer empty")
void isNotEmpty() {
assertThat(stack.isEmpty()).isFalse();
}
@Test
@DisplayName("returns the element when popped and is empty")
void returnElementWhenPopped() {
assertThat(stack.pop()).isEqualTo(anElement);
assertThat(stack.isEmpty()).isTrue();
}
}
@BeforeAll
의 경우 @BeforeAll
과 @AfterAll
을 붙인 메소드는 static
으로 선언되어한다. 그렇게 되면 클래스 내 지역변수를 사용하지 못하게 되므로 예시에서 인스턴스 변수를 사용할 수가 없다. 따라서 @TestInstance(TestInstance.Lifecycle.PER_CLASS)
를 붙여줬다. 이에 대한 설명은 아래에. 이렇게 하면 @BeforeAll
과 @AfterAll
도 static 메소드가 아닌 곳에도 붙일 수 있다. @DisplayName("A stack")
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class BeforeAllTest {
Stack<Object> stack = new Stack<>();
String anElement = "an element";
@BeforeAll
void createNewStack() {
stack.push(anElement);
}
@Test
@DisplayName("it is not empty")
void isNotEmpty() {
assertThat(stack.isEmpty()).isFalse();
}
@Test
@DisplayName("returns the element when popped and is empty")
void returnElementWhenPopped() {
assertThat(stack.pop()).isEqualTo(anElement);
assertThat(stack.isEmpty()).isTrue();
}
}
pop
을 해주었기 때문에 빈 stack의 형태에서 다시 두 번째 테스트를 했기 때문에 테스트 실패@AfterEach
@AfterAll
의 차이도 똑같다 @TestInstance
모든 테스트가 하나의 테스트 환경 내에서 실행되고자 할 때사용한다
JUnit은 기본적으로 각각의 test 메소드 실행 전에 test 클래스 인스턴스를 만든다. ("per-method" test instance lifecycle)
그래서 인스턴스 변수를 선언해두고 각각의 테스트에서 이 변수 값을 변경해도 다른 테스트에는 영향을 미치치 않는다.
@DisplayName("A stack")
public class LifecycleTest {
Stack<Object> stack = new Stack<>();
String anElement = "an element";
@Test
@DisplayName("push an element and is no longer empty")
void isNotEmpty() {
stack.push(anElement);
assertThat(stack.isEmpty()).isFalse();
}
@Test
@DisplayName("it is empty")
void void isEmpty() { {
stack.pop();
assertThat(stack.isEmpty()).isTrue();
}
}
메소드끼리 영향을 주는 테스트를 만드려면 ( 하나의 공동의 테스트 환경 내에서 테스트를 실행하려면) @TestInstance
를 사용한다.
@TestInstance(Lifecycle.PER_CLASS)
과 @TestInstance(Lifecycle.PER_METOD)
이렇게 두 가지로 사용할 수 있는데 후자는 디폴트이므로 굳이 써주지 않아도 된다.
이렇게 @TestInstance(Lifecycle.PER_CLASS)
클래스 단위로 선언해주면 같은 테스트 환경을 메소드끼리 공유하게 된다.
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
로 테스트 순서를 지정해주었다. @DisplayName("A stack")
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class LifecycleTestPerClass {
Stack<Object> stack = new Stack<>();
String anElement = "an element";
@Test
@DisplayName("push an element and is no longer empty")
@Order(1)
void isNotEmpty() {
stack.push(anElement);
assertThat(stack.isEmpty()).isFalse();
}
@Test
@DisplayName("it is empty")
@Order(2)
void isEmpty() {
stack.pop();
assertThat(stack.isEmpty()).isTrue();
}
}
@Disabled
임시로 테스트를 비활성화 하게 해주는 어노테이션
public class ExampleTest {
@Test
@Disabled("잠시 테스트를 비활성화")
void shouldShowSimpleWrongAssertion() {
Assertions.assertEquals(1, 2);
}
@Test
void shouldShowSimpleAssertion() {
Assertions.assertEquals(1, 1);
}
}
@DisplayName
@Test
@DisplayName("Should demonstrate a simple assertion")
void shouldShowSimpleAssertion() {
Assertions.assertEquals(1, 1);
}
@ParameterizedTest
AssertAll
이 여러 assertions
을 확인하는 방법이었다면 @ParameterizedTest
를 이용하면 된다 @ParameterizedTest
를 사용하고 싶다면 dependency에 testCompile 'org.junit.jupiter:junit-jupiter-params:5.7.0'
을 추가해준다 (gradle의 경우) ctrl + shift + o
(dependecy 변경 사항 반영) @ParameterizedTest
를 사용할 떄는 @Test
를 사용하지 않는다@ValueSource
로 data를 array에 담아 전달할 수 있다. 그 외에 null과 빈 데이터소스를 전달하는 방식인 @NullSource
, @EmptySource
, @NullAndEmptySource
가 있고 Enum
을 활용해서 데이터를 전달하는 방식인 @EnumSource
가 있다. 팩토리 메서드를 사용해서 데이터를 전달하는 방식인 @MethodSource
가 있다. 그 외에도 ,
로 구분된 데이터 묶음을 전달하는 @CsvSource
등이 있으니 자세한 사항은 출처를 참고@CsvSource
예시 @ParameterizedTest
@CsvSource({
"apple, 1",
"banana, 2",
"'lemon, lime', 0xF1"
})
void testWithCsvSource(String fruit, int rank) {
assertNotNull(fruit);
assertNotEquals(0, rank);
}
@ValueSource
로 값들이 홀수인지 확인하는 테스트를 해보았다.@ParameterizedTest
@DisplayName("Should check all numbers are odd numbers")
@ValueSource(ints = {3, 4, 5, 8, 14})
void shouldCheckOddNumbers(int number) {
assertThat(number%2).isEqualTo(1);
}
package bongf.junit5;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import java.util.Stack;
import static org.junit.jupiter.api.Assertions.*;
@DisplayName("A stack")
public class NestedTest {
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());
}
@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());
}
}
}
}