Kotest 시스템 확장(System Extensions)
시스템 확장
java.lang.System
을 사용하는 코드를 테스트해야 하는 경우, Kotest는 시스템을 변경하고 각 테스트 후에 복원할 수 있는 확장 기능을 제공한다. 이 확장은 JVM에서만 사용할 수 있다.
이 확장을 사용하려면 프로젝트에 종속성을 추가한다:
io.kotest:kotest-extensions-jvm:${version}
CAUTION
이 확장 기능은 동시(concurrent) 테스트 실행을 지원하지 않는다. JVM 사양으로 인해 이러한 확장은 하나의 인스턴스만 실행할 수 있다(예: 환경 맵이 하나만 존재해야 함). 한 번에 두 개 이상의 인스턴스를 실행하려고 하면 결과가 정의되지 않는다.시스템 환경
시스템 환경 확장을 사용하면 시스템 환경이 어떻게 작동하는지 시뮬레이션할 수 있다. 즉, System.getenv()
에서 얻는 것을 시뮬레이션할 수 있다.
Kotest는 특정 범위의 시스템 환경을 제공하는 몇 가지 확장 함수를 제공한다:
withEnvironment("FooKey", "BarValue") {
System.getenv("FooKey") shouldBe "BarValue" // System environment overridden!
}
INFO
JDK17에서 withEnvironment
를 사용하려면 테스트를 실행하는 JVM의 인수에 --add-opens=java.base/java.util=ALL-UNNAMED
를 추가해야 한다.
Gradle로 테스트를 실행하는 경우 build.gradle.kts
에 다음과 같이 추가한다:
tasks.withType<Test>().configureEach {
jvmArgs("--add-opens=java.base/java.util=ALL-UNNAMED")
}
이 확장 기능에서는 Map이나 Pair 목록을 통해 여러 값을 사용할 수도 있다.
withEnvironment(mapOf("FooKey" to "BarValue", "BarKey" to "FooValue")) {
// Use FooKey and BarKey
}
이 함수는 키와 값이 현재 환경에 없는 경우 추가하고, 있는 경우 재정의한다. 함수에 의해 건드리지 않은 키는 환경에 그대로 유지되며 변경되지 않는다.
확장 함수 대신 제공된 리스너를 사용하여 더 넓은 범위에서 이러한 기능을 적용할 수도 있다. Spec/Per 테스트 레벨에 대한 대안과 프로젝트 레벨에 대한 대안이 있다.
import io.kotest.core.spec.style.FreeSpec
import io.kotest.extensions.system.SystemEnvironmentTestListener
import io.kotest.matchers.shouldBe
class MyTest : FreeSpec() {
override fun listeners() = listOf(SystemEnvironmentTestListener("foo", "bar"))
init {
"MyTest" {
System.getenv("foo") shouldBe "bar"
}
}
}
import io.kotest.core.config.AbstractProjectConfig
import io.kotest.extensions.system.SystemEnvironmentProjectListener
class ProjectConfig : AbstractProjectConfig() {
override fun listeners() = listOf(SystemEnvironmentProjectListener("foo", "bar"))
}
시스템 프로퍼티 확장
환경 확장과 같은 방식으로 시스템 프로퍼티(System.getProperties()
)를 재정의할 수 있다:
withSystemProperty("foo", "bar") {
System.getProperty("foo") shouldBe "bar"
}
그리고 리스너으로도 제공된다:
import io.kotest.core.spec.style.FreeSpec
import io.kotest.extensions.system.SystemPropertyTestListener
import io.kotest.matchers.shouldBe
class MyTest : FreeSpec() {
override fun listeners() = listOf(SystemPropertyTestListener("foo", "bar"))
init {
"MyTest" {
System.getProperty("foo") shouldBe "bar"
}
}
}
시스템 보안 관리자
마찬가지로 시스템 보안 관리자를 사용하면 시스템 보안 관리자(System.getSecurityManager()
)를 재정의할 수 있다.
withSecurityManager(myManager) {
// Usage of security manager
}
그리고 리스너으로도 제공된다:
import io.kotest.core.spec.style.FreeSpec
import io.kotest.extensions.system.SecurityManagerListener
class MyTest : FreeSpec() {
override fun listeners() = listOf(SecurityManagerListener(myManager))
init {
// Use my security manager
}
}
시스템 종료 확장
때때로 코드가 System.exit
를 호출하는지 테스트하고 싶을 때가 있다. 이를 위해 시스템 종료 리스너를 사용할 수 있다. 리스너는 System.exit
가 호출될 때 예외를 던지므로 이를 포착하고 확인할 수 있다:
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.style.FreeSpec
import io.kotest.extensions.system.SpecSystemExitListener
import io.kotest.extensions.system.SystemExitException
import io.kotest.matchers.shouldBe
class MyTest : FreeSpec() {
override fun listeners() = listOf(SpecSystemExitListener)
init {
"Catch exception" {
val thrown: SystemExitException = shouldThrow<SystemExitException> {
System.exit(22)
}
thrown.exitCode shouldBe 22
}
}
}
no-stdout/no-stderr 리스너
디버그 메시지를 남기지 않았거나 로깅에 항상 로거를 사용하고 있는지 확인하고 싶을 수도 있다.
이를 위해 Kotest는 NoSystemOutListener
와 NoSystemErrListener
를 제공한다. 이러한 리스너는 각각 System.out
또는 System.err
에 메시지를 바로 출력하는 것을 허용하지 않는다:
// In Project or in Spec
override fun listeners() = listOf(NoSystemOutListener, NoSystemErrListener)
Locale/Timezone 리스너
일부 코드는 기본 Locale 및 Timezone를 사용하거나 이에 민감하다. 시스템 기본값을 직접 조작하는 대신 Kotest가 대신 처리해 준다.
withDefaultLocale(Locale.FRANCE) {
println("My locale is now France! Très bien!")
}
withDefaultTimeZone(TimeZone.getTimeZone(ZoneId.of("America/Sao_Paulo"))) {
println("My timezone is now America/Sao_Paulo! Muito bem!")
}
그리고 리스너으로도 제공된다:
// In Project or in Spec
override fun listeners() = listOf(
LocaleTestListener(Locale.FRANCE),
TimeZoneTestListener(TimeZone.getTimeZone(ZoneId.of("America/Sao_Paulo")))
)