extend kotest DescribeSpec
Note with example on how to extend a DescribeSpec
in kotest.
GT-Sandbox-Snapshot: Extend DescribeSpec to add interception (before & after test)
In the following example we extend and intercept (related kotest DescribeSpec interception)
Code
package com.glassthought.sandbox
import io.kotest.core.extensions.TestCaseExtension
import io.kotest.core.spec.style.DescribeSpec
import io.kotest.core.test.TestCase
import io.kotest.core.test.TestResult
import gt.sandbox.util.output.Out
import com.glassthought.sandbox.sandbox.util.out.impl.OutSettings
val out = Out.standard(OutSettings(printCoroutineName = true))
// Custom extension for test case interception
class LoggingTestCaseExtension : TestCaseExtension {
override suspend fun intercept(
testCase: TestCase,
execute: suspend (TestCase) -> TestResult
): TestResult {
out.printlnBlue("Before executing test: ${testCase.name.testName}")
val result = execute(testCase)
out.printlnGreen("After executing test: ${testCase.name.testName}")
return result
}
}
// Custom DescribeSpec class
abstract class CustomDescribeSpec(body: CustomDescribeSpec.() -> Unit = {}) : DescribeSpec() {
init {
extension(LoggingTestCaseExtension()) // Add logging extension
body() // Execute the user-defined spec body
}
}
class MyCustomDescribeSpec : CustomDescribeSpec({
describe("outer-describe") {
describe("nested-describe-1") {
it("it-in-nested-describe") {
out.println("Running test inside inner describe")
}
}
it("test-1 in outer describe") {
out.println("Running test-1 in outer describe")
}
it("test-2 in outer describe") {
out.println("Running test-2 in outer describe")
}
}
})
Command to reproduce:
gt.sandbox.checkout.commit f26ddf425b14c7868de8 \
&& cd "${GT_SANDBOX_REPO}" \
&& cmd.run.announce "./gradlew test --rerun-tasks"
Recorded output of command:
> Task :app:checkKotlinGradlePluginConfigurationErrors SKIPPED
> Task :app:processResources NO-SOURCE
> Task :app:processTestResources NO-SOURCE
> Task :app:compileKotlin
> Task :app:compileJava NO-SOURCE
> Task :app:classes UP-TO-DATE
> Task :app:compileTestKotlin
> Task :app:compileTestJava NO-SOURCE
> Task :app:testClasses UP-TO-DATE
> Task :app:test
Gradle Test Executor 2 STANDARD_OUT
Warning: Kotest autoscan is enabled. This means Kotest will scan the classpath for extensions that are annotated with @AutoScan. To avoid this startup cost, disable autoscan by setting the system property 'kotest.framework.classpath.scanning.autoscan.disable=true'. In 6.0 this value will default to true. For further details see https://kotest.io/docs/next/framework/project-config.html#runtime-detection
com.glassthought.sandbox.MyCustomDescribeSpec STANDARD_OUT
Before executing test: outer-describe
com.glassthought.sandbox.MyCustomDescribeSpec > outer-describe STANDARD_OUT
Before executing test: nested-describe-1
com.glassthought.sandbox.MyCustomDescribeSpec > outer-describe > nested-describe-1 STANDARD_OUT
Before executing test: it-in-nested-describe
com.glassthought.sandbox.MyCustomDescribeSpec > outer-describe > nested-describe-1 > com.glassthought.sandbox.MyCustomDescribeSpec.it-in-nested-describe STARTED
com.glassthought.sandbox.MyCustomDescribeSpec > outer-describe > nested-describe-1 > com.glassthought.sandbox.MyCustomDescribeSpec.it-in-nested-describe STANDARD_OUT
Running test inside inner describe
After executing test: it-in-nested-describe
com.glassthought.sandbox.MyCustomDescribeSpec > outer-describe > nested-describe-1 > com.glassthought.sandbox.MyCustomDescribeSpec.it-in-nested-describe PASSED
com.glassthought.sandbox.MyCustomDescribeSpec > outer-describe > nested-describe-1 STANDARD_OUT
After executing test: nested-describe-1
com.glassthought.sandbox.MyCustomDescribeSpec > outer-describe STANDARD_OUT
Before executing test: test-1 in outer describe
com.glassthought.sandbox.MyCustomDescribeSpec > outer-describe > test-1 in outer describe STARTED
com.glassthought.sandbox.MyCustomDescribeSpec > outer-describe > test-1 in outer describe STANDARD_OUT
Running test-1 in outer describe
After executing test: test-1 in outer describe
com.glassthought.sandbox.MyCustomDescribeSpec > outer-describe > test-1 in outer describe PASSED
com.glassthought.sandbox.MyCustomDescribeSpec > outer-describe STANDARD_OUT
Before executing test: test-2 in outer describe
com.glassthought.sandbox.MyCustomDescribeSpec > outer-describe > test-2 in outer describe STARTED
com.glassthought.sandbox.MyCustomDescribeSpec > outer-describe > test-2 in outer describe STANDARD_OUT
Running test-2 in outer describe
After executing test: test-2 in outer describe
com.glassthought.sandbox.MyCustomDescribeSpec > outer-describe > test-2 in outer describe PASSED
com.glassthought.sandbox.MyCustomDescribeSpec > outer-describe STANDARD_OUT
After executing test: outer-describe
BUILD SUCCESSFUL in 3s
3 actionable tasks: 3 executed
GT-Sandbox-Snapshot: Extend with before and after entire spec
Code
package com.glassthought.sandbox
import io.kotest.core.extensions.TestCaseExtension
import io.kotest.core.spec.style.DescribeSpec
import io.kotest.core.test.TestCase
import io.kotest.core.test.TestResult
import gt.sandbox.util.output.Out
import com.glassthought.sandbox.sandbox.util.out.impl.OutSettings
import io.kotest.core.spec.Spec
val out = Out.standard(OutSettings(printCoroutineName = true))
// Custom extension for test case interception
class LoggingTestCaseExtension : TestCaseExtension {
override suspend fun intercept(
testCase: TestCase,
execute: suspend (TestCase) -> TestResult
): TestResult {
out.printlnBlue("Before executing test: ${testCase.name.testName}")
val result = execute(testCase)
out.printlnGreen("After executing test: ${testCase.name.testName}")
return result
}
}
// Custom DescribeSpec class
abstract class CustomDescribeSpec(body: CustomDescribeSpec.() -> Unit = {}) : DescribeSpec() {
init {
extension(LoggingTestCaseExtension()) // Add logging extension
body() // Execute the user-defined spec body
}
override suspend fun beforeSpec(spec: Spec) {
super.beforeSpec(spec)
out.printlnOrange("Before the entire spec: ${spec::class.simpleName}")
}
override suspend fun afterSpec(spec: Spec) {
super.afterSpec(spec)
out.printlnOrange("After the entire spec: ${spec::class.simpleName}")
}
}
class MyCustomDescribeSpec : CustomDescribeSpec({
describe("outer-describe") {
describe("nested-describe-1") {
it("it-in-nested-describe") {
out.println("Running test inside inner describe")
}
}
it("test-1 in outer describe") {
out.println("Running test-1 in outer describe")
}
it("test-2 in outer describe") {
out.println("Running test-2 in outer describe")
}
}
})
Command to reproduce:
gt.sandbox.checkout.commit 6784418679fee923d907 \
&& cd "${GT_SANDBOX_REPO}" \
&& cmd.run.announce "./gradlew test --rerun-tasks"
Recorded output of command:
> Task :app:checkKotlinGradlePluginConfigurationErrors SKIPPED
> Task :app:processResources NO-SOURCE
> Task :app:processTestResources NO-SOURCE
> Task :app:compileKotlin
> Task :app:compileJava NO-SOURCE
> Task :app:classes UP-TO-DATE
> Task :app:compileTestKotlin
> Task :app:compileTestJava NO-SOURCE
> Task :app:testClasses UP-TO-DATE
> Task :app:test
Gradle Test Executor 2 STANDARD_OUT
Warning: Kotest autoscan is enabled. This means Kotest will scan the classpath for extensions that are annotated with @AutoScan. To avoid this startup cost, disable autoscan by setting the system property 'kotest.framework.classpath.scanning.autoscan.disable=true'. In 6.0 this value will default to true. For further details see https://kotest.io/docs/next/framework/project-config.html#runtime-detection
com.glassthought.sandbox.MyCustomDescribeSpec STANDARD_OUT
Before the entire spec: MyCustomDescribeSpec
Before executing test: outer-describe
com.glassthought.sandbox.MyCustomDescribeSpec > outer-describe STANDARD_OUT
Before executing test: nested-describe-1
com.glassthought.sandbox.MyCustomDescribeSpec > outer-describe > nested-describe-1 STANDARD_OUT
Before executing test: it-in-nested-describe
com.glassthought.sandbox.MyCustomDescribeSpec > outer-describe > nested-describe-1 > com.glassthought.sandbox.MyCustomDescribeSpec.it-in-nested-describe STARTED
com.glassthought.sandbox.MyCustomDescribeSpec > outer-describe > nested-describe-1 > com.glassthought.sandbox.MyCustomDescribeSpec.it-in-nested-describe STANDARD_OUT
Running test inside inner describe
After executing test: it-in-nested-describe
com.glassthought.sandbox.MyCustomDescribeSpec > outer-describe > nested-describe-1 > com.glassthought.sandbox.MyCustomDescribeSpec.it-in-nested-describe PASSED
com.glassthought.sandbox.MyCustomDescribeSpec > outer-describe > nested-describe-1 STANDARD_OUT
After executing test: nested-describe-1
com.glassthought.sandbox.MyCustomDescribeSpec > outer-describe STANDARD_OUT
Before executing test: test-1 in outer describe
com.glassthought.sandbox.MyCustomDescribeSpec > outer-describe > test-1 in outer describe STARTED
com.glassthought.sandbox.MyCustomDescribeSpec > outer-describe > test-1 in outer describe STANDARD_OUT
Running test-1 in outer describe
After executing test: test-1 in outer describe
com.glassthought.sandbox.MyCustomDescribeSpec > outer-describe > test-1 in outer describe PASSED
com.glassthought.sandbox.MyCustomDescribeSpec > outer-describe STANDARD_OUT
Before executing test: test-2 in outer describe
com.glassthought.sandbox.MyCustomDescribeSpec > outer-describe > test-2 in outer describe STARTED
com.glassthought.sandbox.MyCustomDescribeSpec > outer-describe > test-2 in outer describe STANDARD_OUT
Running test-2 in outer describe
After executing test: test-2 in outer describe
com.glassthought.sandbox.MyCustomDescribeSpec > outer-describe > test-2 in outer describe PASSED
com.glassthought.sandbox.MyCustomDescribeSpec > outer-describe STANDARD_OUT
After executing test: outer-describe
com.glassthought.sandbox.MyCustomDescribeSpec STANDARD_OUT
After the entire spec: MyCustomDescribeSpec
BUILD SUCCESSFUL in 2s
3 actionable tasks: 3 executed