[F-Lab 모각코 챌린지 63일차] @BeforeAll, @AfterAll Non-static method로 구현하기

부추·2023년 8월 2일
2

F-Lab 모각코 챌린지

목록 보기
63/66

문제 상황 : Test Method끼리 공유하는 Bean 필요

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를 돌리면 에러가 뜬다.



에러 이유 : Repository는 static이 될 수 없다

로그를 살펴보자.
@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하는 방법도 있긴 하다. 사실 이쪽이 더 간단한듯 ㄱ-.



REFERENCE

https://www.baeldung.com/java-beforeall-afterall-non-static

https://blog.joonas.io/225

profile
부추튀김인지 부추전일지 모를 정도로 빠싹한 부추전을 먹을래

0개의 댓글