조급하면 모래성이 될뿐

Kotest + TestContainers Global 설정 본문

구현 기록/TestContiners

Kotest + TestContainers Global 설정

Pawer0223 2023. 6. 24. 01:15

domain 모듈에서 mariadb컨테이너가 필요한 단위테스트 할 때, properties 설정만으로 애플리케이션이 시작될 때마다 컨테이너화된 새로운 데이터베이스 인스턴스를 얻을 수 있었다. - JDBC support

 

api 모듈에서는 추가적으로 redis 컨테이너도 필요했다. 위처럼 설정 하나로 모든 테스트에서 Redis 컨테이너를 참조하고 싶었지만... 테스트 컨테이너를 다루는 일반적인 방식으로는 불가능했다.

 

싱글톤으로 테스트 컨테이너를 만드는 일반적인 방식

- static final 로 선언된 컨테이너를 관리하는 추상 클래스를 정의한다.

- 컨테이너가 필요한 클래스에서 이 추상 클래스를 상속하여 사용한다.

- 공식문서

abstract class AbstractContainerBaseTest {

    static final MySQLContainer MY_SQL_CONTAINER;

    static {
        MY_SQL_CONTAINER = new MySQLContainer();
        MY_SQL_CONTAINER.start();
    }
}

class FirstTest extends AbstractContainerBaseTest {

    @Test
    void someTestMethod() {
        String url = MY_SQL_CONTAINER.getJdbcUrl();

        // create a connection and run test as normal
    }
}

 

 

이렇게 TestContainer를 클래스 당 하나씩 사용하는 이유는 테스트 간의 독립성과 안정성을 확보하기 위해서인 것 같다.

(공식 문서에서 관련내용은 못찾았고.. Chat GPT를 통해 얻었다.)

 

일반적으로 테스트는 각자의 독립된 상태에서 실행되어야 합니다. 테스트 간에 상태를 공유하거나 의존성을 가지면 예기치 않은 결과가 발생할 수 있습니다. 이러한 문제를 방지하기 위해 TestContainer를 클래스 단위로 사용합니다.
TestContainer는 테스트에 필요한 환경을 구성하는 역할을 합니다. 각 클래스에 해당하는 TestContainer를 사용하면 특정 테스트 클래스의 테스트 케이스들은 독립적인 환경에서 실행됩니다. 이렇게 하면 하나의 테스트가 실패하더라도 다른 테스트에는 영향을 미치지 않습니다. 또한, 각 테스트가 독립적인 데이터를 사용하므로 테스트 간의 데이터 충돌이나 부작용도 최소화됩니다.
따라서, TestContainer를 클래스 당 하나씩 사용함으로써 테스트의 독립성과 안정성을 보장할 수 있습니다. 이는 테스트의 신뢰성을 높이고 효과적인 테스트 스위트를 작성하는 데 도움이 됩니다.

 

그렇다 보니.. 필요한 컨테이너를 미리 싱글톤으로 정의해 두고, 클래스 단위로 상속해서 사용하는 것 같다.

 

이러한 구조는 kotest와 함께 사용할 때 치명적인 문제가 존재했다. kotest는 테스트 스타일 중 하나를 상속해서 사용하는 구조다. 그러다 보니 컨테이너를 위한 설정을 추가적으로 상속받지 못한다는 것이었다..

 

진짜 무식하게 모든 클래스에 companion object를 정의해서 사용할 순 있겠지만.. 이건 너무 비효율적인 것 같았다. 결국 클래스 단위로 컨테이너를 먼저 생성하면 되기 때문에.. kotest에서 @beforeTest와 같은 기능을 전역적으로 설정할 수 있는 기능을 찾아보고자 했다.

 

결론적으로는 Kotest의 확장 프로그램을 사용해 재사용 가능한 Lifecycle hooks를 정의해서 사용했다.

 

- Lifecycle hooks

- Extensions

 

여러 리스너가 있지만.. 그중 Project Level로 테스트 컨테이너를 설정했다. 물론.. 테스트 품질(?)을 위해서 클래스 단위로 테스트 컨테이너를 실행, 중지할 수 있었다. (BeforeTestLister) 그런데 이렇게 설정했더니 너무 느렸다. (계속 컨테이너 받고.. 끄고..) 또한, 처음 컨테이너를 적용하고자 했을때 프로젝트 범위를 의도했기 때문에 ProjectLister를 사용했다.

 

정리

testContainers에서 권장하는 상속을 통한 싱글톤 컨테이너 관리 방식은 kotest에서 제약이 있다. (다중상속 불가)

따라서 프로젝트 Level로 컨테이너를 실행, 중지할 수 있도록 kotest의 확장 프로그램을 사용해 해결했다.

 

기타

테스트 컨테이너 사용하면서 동적으로 프로퍼티를 설정해야 했다. spring.datasource.url이나, spring.redis.host 등등..

@DynamicPropertySource를 사용하려 시도했으나.. 잘 되지 않았다.. 우선 System.setProperty를 통해 해결했다.

+ maria DB 같은 경우는  컨테이너 만들 때 유저네임과 Password를 지정해 주자. 유저 네임은 (root)로 두면 안될 수도 있다.

override suspend fun beforeProject() {
    mariaDBContainer.start()
    System.setProperty("spring.datasource.url", mariaDBContainer.jdbcUrl)
    System.setProperty("spring.datasource.username", mariaDBContainer.username)
    System.setProperty("spring.datasource.password", mariaDBContainer.password)

    redisContainer.start()
    System.setProperty("spring.redis.host", redisContainer.host)
    System.setProperty("spring.redis.port", redisContainer.getMappedPort(REDIS_PORT).toString())
}
반응형