Autowiring in Spring Integration Tests with Kotest
test kotlin kotest
Autowiring in Spring Integration Tests with Kotest
Dependency:
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("io.kotest:kotest-runner-junit5:$kotestVersion")
testImplementation("io.kotest.extensions:kotest-extensions-spring:$kotestExtensionsVersion")
@SpringBootTest + SpringExtension 을 같이 사용해야지 Autowired 가 정상적으로 동작한다.
@SpringBootTest
class BookingDistributedLockConcurrencyIntegrationTest: DescribeSpec() {
override fun extensions() = listOf(SpringExtension)
@Autowired
private lateinit var bookingController: BookingController
@Autowired
private lateinit var bookingFacade: BookingFacade
@Autowired
private lateinit var bookingRepository: BookingRepository
init {
describe("...") {
...
}
}
}
SpringExtension 은 Project Level Config 로 구성할 수 있다.
SpringTestExtension
val SpringExtension = SpringTestExtension(SpringTestLifecycleMode.Test)
class SpringTestExtension(private val mode: SpringTestLifecycleMode = SpringTestLifecycleMode.Test) : TestCaseExtension,
SpecExtension {
var ignoreSpringListenerOnFinalClassWarning: Boolean = false
// Spring 컨텍스트 초기화 및 정리를 Kotest 의 Spec 또는 TestCase 실행 라이프사이클에 맞춰 처리
override suspend fun intercept(spec: Spec, execute: suspend (Spec) -> Unit) {
safeClassName(spec::class)
val context = TestContextManager(spec::class.java)
withContext(SpringTestContextCoroutineContextElement(context)) {
testContextManager().beforeTestClass()
testContextManager().prepareTestInstance(spec)
execute(spec)
testContextManager().afterTestClass()
}
}
override suspend fun intercept(testCase: TestCase, execute: suspend (TestCase) -> TestResult): TestResult {
val methodName = method(testCase)
if (testCase.isApplicable()) {
testContextManager().beforeTestMethod(testCase.spec, methodName)
testContextManager().beforeTestExecution(testCase.spec, methodName)
}
val result = execute(testCase)
if (testCase.isApplicable()) {
testContextManager().afterTestExecution(testCase.spec, methodName, result.errorOrNull)
testContextManager().afterTestMethod(testCase.spec, methodName, result.errorOrNull)
}
return result
}
/**
* Returns true if this test case should have the spring lifecycle methods applied
*/
private fun TestCase.isApplicable() = (mode == SpringTestLifecycleMode.Root && isRootTest()) ||
(mode == SpringTestLifecycleMode.Test && type in arrayOf(TestType.Test, TestType.Dynamic))
...
- Spring 의 테스트 컨텍스트 라이프사이클을 정확히 준수하며, 필요에 따라 클래스 수준이나 테스트 수준에서 컨텍스트를 재사용한다.
- SpringTestLifecycleMode 를 통해 개발자가 원하는 방식으로 컨텍스트 초기화 및 종료를 제어할 수 있다.
testContextManager().beforeTestClass()
testContextManager().prepareTestInstance(spec)
execute(spec)
testContextManager().afterTestClass()
Spring 컨텍스트 라이프사이클을 제어:
- beforeTestClass: 테스트 클래스 실행 전에 Spring 컨텍스트 초기화
- prepareTestInstance: 현재 Spec 에 대한 테스트 인스턴스 초기화
- execute(spec): Spec 실행
- afterTestClass: Spec 실행 후 컨텍스트 정리