JUnit5를 이용해 Spring Boot 프로젝트의 테스트코드를 작성하는 도중이었다. 하나의 @SpringBootTest
클래스 안에 존재하는 여러 개의 @Test
method가 공유하는 Repository를 생성하고 싶었다.
코드로 말하면 아래와 같은 상황이다. @BeforeAll
이 붙은 method가 테스트 method들을 실행하기 전 10명의 유저를 userRepository에 저장한다. 그 후 2개의 test method가 userRepository에 저장된 것을 테스트하는 것이다.
@SpringBootTest
public class EntityTest {
@Autowired
private UserRepository userRepository;
@BeforeAll
public void setDB() {
for (int i = 1; i <= 10; i++) {
User user = User.builder().name("유저 "+i).build();
userRepository.save(user);
}
}
@Test
public void testUserNum() {
assertEquals(10, userRepository.findAll().size());
}
@Test
public void testUserType() {
for (User user : userRepository.findAll()) {
assertInstanceOf(User.class, user);
}
}
위 Test Class를 돌리면 에러가 뜬다.
로그를 살펴보자.
@BeforeAll
어노테이션이 붙은 method는 static이어야 한다고? @BeforeAll
문서에도 같은 내용이 있다.
JUnit5에서, 테스트 클래스의 life cycle은 기본적으로
PER_METHOD
로 설정되어 있다. 테스트 클래스 내부의 테스트 method들이 실행될 때마다 테스트 클래스가 새로 생성되고 삭제되는 것이다. 때문에 static으로 선언되지 않는 한 테스트 method들이 공유하는 클래스 내부 요소들을 관리할 수는 없다.
@TestInstance(TestInstance.LifeCycle.PER_CLASS)
그렇다면 테스트 클래스의 life cycle을 바꿔야한다. EntityTest 클래스에 @TestInstance(TestInstance.LifeCycle.PER_CLASS)를 붙여 테스트 클래스의 생명주기를 클래스 단위로 바꿔주자.
@SpringBootTest
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class EntityTest {
@Autowired
private UserRepository userRepository;
@BeforeAll
public void setDB() {
for (int i = 1; i <= 10; i++) {
User user = User.builder().name("유저 "+i).build();
userRepository.save(user);
}
}
@Test
public void testUserNum() {
assertEquals(10, userRepository.findAll().size());
}
@Test
public void testUserType() {
for (User user : userRepository.findAll()) {
assertInstanceOf(User.class, user);
}
}
그럼 테스트 method가 몇 개이든 테스트 클래스는 한 번만 생성될 것이니 @BeforeAll, @AfterAll method가 static일 필요는 없게 된다. testUserNum(), testUserType()은 동일한 userRepository를 공유한다. 실제로 위 테스트 클래스는 별 문제 없이 잘 실행된다.
그렇다면 @TestInstance(TestInstance.LifeCycle.PER_CLASS)
를 붙인 테스트 클래스에서 각각의 method들은 언제 실행될까? 위 접은글과 같은 환경에서 생명주기를 바꾸는 어노테이션만 붙이고 실행해보면 출력되는 결과는 아래와 같다.
일단 constructor는 프로그램 시작과 동시에 실행됐다.
생명주기가 PER_METHOD였을 때는 constructor 위에 @BeforAll
method가 찍혔지만,
생명주기가 PER_CLASS로 바뀐 지금은 @BeforeAll method가 class 생성 이후에 실행됨을 확인했다. 더불어 construt 로그가 한 번만 찍혔다. @Test method가 여러개더라도 새로운 테스트 클래스가 생성되지 않았다.
생명주기에 따라 테스트 클래스의 각 method들의 호출 시점을 정리해놓은 그림이다.
별개로 굳이 생명주기를 건들지 않고 @BeforeEach, @AfterEach를 사용하여 각 테스트 method마다 DB데이터를 save/delete하는 방법도 있긴 하다. 사실 이쪽이 더 간단한듯 ㄱ-.