라이프 사이클
JUnit의 중요한 특성 중 하나는 테스트들을 실행할 때 각 테스트 클래스의 새 인스턴스를 만들어 각각 독립적으로 실행되는 것입니다. 이를 통해 테스트 인스턴스의 예상치 못한 부작용을 피할 수 있습니다.
public class MethodLifecycleTest {
int count = 1;
@BeforeAll
static void init() {
System.out.println("===테스트 시작===");
}
@AfterAll
static void destroy() {
System.out.println("===테스트 종료===");
}
@DisplayName("테스트1")
@Test
void plus1() throws Exception{
System.out.println(++count); // 결과 = 2
System.out.println(this); // 인스턴스a
}
@DisplayName("테스트2")
@Test
void plus2() throws Exception{
System.out.println(++count); // 결과 = 2
System.out.println(this); // 인스턴스b
}
@DisplayName("테스트3")
@Test
void plus3() throws Exception{
System.out.println(++count); // 결과 = 2
System.out.println(this); // 인스턴스c
}
}
해당 코드를 보면, 클래스의 생명주기(Life Cycle)가 각 메서드까지 인 것을 알 수 있습니다.
만약 클래스가 stateless 해 불필요한 인스턴스 생성을 피하고 싶은 경우(굳이 독립적일 필요가 없을 때)에는 이 생명주기를 '@TestInstance'를 통해 클래스 단위로 변경할 수 있습니다.
@TestInstance(TestInstance.Lifecycle.PER_CLASS) // 라이프사이클을 클래스레벨로 올림
public class MethodLifecycleTest {
int count = 1;
// 생명주기가 클래스레벨일 경우에는 @BeforeAll @AfterAll에 static 선언을 하지 않아도 된다.
@BeforeAll
void init() {
System.out.println("===테스트 시작===");
}
@AfterAll
void destroy() {
System.out.println("===테스트 종료===");
}
@DisplayName("테스트1")
@Test
void plus1() throws Exception{
System.out.println(++count); // 결과 = 2
System.out.println(this); // 인스턴스a
}
@DisplayName("테스트2")
@Test
void plus2() throws Exception{
System.out.println(++count); // 결과 = 3
System.out.println(this); // 인스턴스a
}
@DisplayName("테스트3")
@Test
void plus3() throws Exception{
System.out.println(++count); // 결과 = 4
System.out.println(this); // 인스턴스a
}
}
여기서 '반드시 재설정해야 하는 인스턴스가 있다.'라고 하면, 이는 @BeforeEach나 @AfterEach에서 재설정해주시면 됩니다!
추가적으로 생명주기가 클래스 레벨인 경우에는 이미 정적이기 때문에 굳이 @BeforeAll이나, @AfterAll에서 static 선언을 하지 않아도 됩니다.
추가적으로 @Nested를 클래스에서 추가적으로 @BeforeAll, @AfterAll을 설정할 수 있습니다. (@BeforeEach, @AfterEach는 계속 공유해서 사용.)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class MethodLifecycleTest {
int count = 1;
@BeforeAll
void init() {
System.out.println("===테스트 시작===");
}
@AfterAll
void destroy() {
System.out.println("===테스트 종료===");
}
@BeforeEach
void initMethod() {
System.out.println("---단위 테스트 시작---");
}
...
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@Nested
class innerTest {
// 각 Nested 클래스에서 재사용이 가능해진다.
@BeforeAll
void initA() {
System.out.println("===이너 클래스 테스트 시작===");
}
@AfterAll
void destroyA() {
System.out.println("===이너 클래스 테스트 시작===");
}
@Test
void anotherTest() {
System.out.println(++count);
System.out.println(this);
}
}
}
당연하지만 Java의 특성상 일반적인 static 선언으로는 불가능합니다.
테스트 메서드 실행 순서
사실 각 테스트 메서드(단위 테스트)들은 실행에 정해진 순서가 없습니다. 무작위로 실행된다는 것입니다!
왜냐하면, 각 단위 테스트들이 독립적으로 수행되어야 하기 때문에, 굳이 순서에 의존할 필요가 없기 때문입니다.
하지만, 함수 구현 테스트나, 통합 테스트 같이 실행 순서를 특정해야 하는 경우도 있습니다. 이런 상황에서 '@TestMethodOrder'을 사용하시면 됩니다. 이 어노테이션의 파라미터는 MethodOrderer의 구현체를 받는데, 이 구현체에 따라 우선순위를 결정할 수 있습니다. JUnit에서는 기본적으로 여러 구현체를 지원합니다.
MethodOrderer 구현체 | 설명 |
Alphanumeric | 이름과 파라미터 목록을 기반으로 테스트 순서를 영숫자 순으로 정렬하는 방법입니다. (6.0에서 MethodName로 기능이 넘어가, 제거될 예정입니다.) |
DisplayName | @DisplayName에 따라 테스트 메서드를 영숫자 순으로 정렬하는 방법입니다. |
MethodName | 이름과 파라미터 목록을 기반으로 테스트 순서를 영숫자 순으로 정렬하는 방법입니다. (Alphanumeric을 대체할 구현체입니다.) |
OrderAnnotation | @Order(int) 어노테이션을 통해 테스트 메서드를 정렬합니다. |
Random | 정해진 로직이 아니라, 무작위로 실행합니다. |
추가적으로 이렇게 순서가 필요한 경우에는, 인스턴스를 공유해야 할 경우가 많기 때문에 '@TestInstance(TestInstance.Lifecycle.PER_CLASS)'의 사용이 선행되어 집니다.
원하는 순서가 없다면 따로 커스텀 순서를 구현할 수 있습니다.
OrderAnnotation만 짚고 넘어갑시다!
어노테이션을 이용한 순서 (OrderAnnotation)
각 테스트 메서드(단위 테스트)에 '@Order(int)'를 이용해 순서를 결정하는 방법입니다.
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class MethodOrderTest {
@BeforeAll
static void init() {
System.out.println("===테스트 시작===");
}
@AfterAll
static void destroy() {
System.out.println("===테스트 종료===");
}
@Test
@Order(2) // 두 번째 실행
void test1() throws Exception{
System.out.println("test1 실행");
System.out.println(this);
}
@Test
@Order(1) // 첫 번째 실행
void test2() throws Exception{
System.out.println("test2 실행");
System.out.println(this);
}
@Test
@Order(3) // 세 번째 실행
void test3() throws Exception{
System.out.println("test3 실행");
System.out.println(this);
}
}
실행 순서 전체 적용
만약 각 클래스가 아닌, 프로젝트의 테스트 전체에 해당 실행 순서를 적용시키고 싶은 경우
'junit-platform.properties'에서 설정이 가능합니다.
예시
# OrderAnnotation 순서방식으로 테스트 실행
junit.jupiter.testmethod.order.default = \
org.junit.jupiter.api.MethodOrderer$OrderAnnotation
'JAVA > JUnit' 카테고리의 다른 글
[JUnit5] 내부동작(1) - 생성자와, 메서드의 매개변수 (0) | 2021.10.12 |
---|---|
[JUnit5] 테스트 작성(6) - Nested Test (0) | 2021.10.12 |
[JUnit5] 테스트 작성(4) - Tag (0) | 2021.10.08 |
[JUnit5] 테스트 작성(3) - Assumptions와 테스트 필터링 (0) | 2021.10.08 |
[JUnit5] 테스트 작성(2) - Assertions (0) | 2021.10.08 |