JUnit 5에서 모든 테스트 전에 코드를 한번만 실행하는 방법

공부는 혼자하는 거·2022년 11월 22일
0

Spring Tip

목록 보기
30/52

Docker를 통해서 TestContainer를 사용 중, 테스트 데이터를 집어넣는 스크립트를 실행하는 코드를 작성했다. 문제는 전체 테스트를 실행하면,

상위 클래스에 @TestInstance(TestInstance.Lifecycle.PER_CLASS) 와 @BeforeAll 를 사용해도, 테스트 클래스마다 인스턴스가 새로 생성되며, 상속받는 부모 클래스의 메서드를 한 번 씩 호출한다는 점. 그래서 매번 테스트 클래스마다 스크립트를 실행하는 코드를 계속 호출하므로 속도가 느리다. 방법을 찾던 중, Junit 확장 인터페이스 중 BeforeAllCallback이 눈에 띄었다.

class TestDataSetup : BeforeAllCallback, ExtensionContext.Store.CloseableResource {

    private var dataSource: DataSource? = null
    private var s3Mock:S3Mock? = null

    private val log = KotlinLogging.logger {  }

    override fun beforeAll(extensionContext: ExtensionContext) {
        synchronized(TestDataSetup::class.java) {
            if (!started) {
                started = true

                // get the dataSource bean from the spring context
                val springContext: ApplicationContext = SpringExtension.getApplicationContext(extensionContext)
                dataSource = springContext.getBean(DataSource::class.java)

                s3Mock = springContext.getBean(S3Mock::class.java)

                try {
                    s3Mock!!.start()
                    dataSource!!.connection.use { conn ->
                        log.info("TEST!!!!!!!!!!!@@@@@@@=>${conn.metaData.url}")
                        ScriptUtils.executeSqlScript(conn, ClassPathResource("/initData/init.sql"))
                    }
                } catch (e: Exception) {
                    e.printStackTrace()
                }

                // register a callback hook for when the root test context is shut down
                extensionContext
                    .getRoot()
                    .getStore(ExtensionContext.Namespace.GLOBAL)
                    .put("TestDataSetup-started", this)
            }
        }
    }

    override fun close() {
        synchronized(TestDataSetup::class.java) {
            log.info("Tear down Amazon")
            s3Mock?.stop()
        }
    }

    companion object {
        private var started = false
    }
}

병렬 처리에 문제가 생길 수 있으므로, synchronized를 붙여주는 게 좋다고 한다.
그리고 한번만 실행하고 싶은 모든 테스트 클래스의 부모 클래스에,

@SpringBootTest(
    properties = ["spring.config.location=classpath:/application-test.yml" , "spring.profiles.active=test"]
)
@ExtendWith(TestDataSetup::class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class SpringTestSupport() {

		
}



그리고 스크립트를 실행하고 싶은 모든 테스트 클래스를 상속시켜준다.

@Transactional
class AuthApiTest(
) : SpringWebTestSupport() {

}


@Transactional
class MemberApiTest(
) : SpringWebTestSupport() {

}

참고

https://stackoverflow.com/questions/43282798/in-junit-5-how-to-run-code-before-all-tests
https://stackoverflow.com/questions/56904620/junit-5-inject-spring-components-to-extension-beforeallcallback-afterallcall/62504238#62504238

profile
시간대비효율

0개의 댓글