kotest nested spec in describe clause

2k Views Asked by At

I've started using kotest:4.0.5 (kotlintest) and having problem with stringSpec function nested in describe clause.

Example:

class SellerTest : DescribeSpec({

    describe("Registration") {
        context("Not existing user") {
            include(emailValidation()
        }
    }
})

fun emailValidation() = stringSpec {
    "Email validation" {
        forAll(
            row("test.com"),
            row("123123123123123")
        ) { email ->
            assertSoftly {
                val exception =
                    shouldThrow<ServiceException> { Email(email) }

            }
        }
    }
}

If include(emailValidation()) is outside describe clause then correctly works.

Have you any idea how to nest specs/functions in clauses?

2

There are 2 best solutions below

3
sksamuel On

You can only use include at the top level. This is part of how factory tests (what the include keyword is used for) are implemented (perhaps that will be relaxed in a future release).

You can move the whole thing into the factory though.

class SellerTest : DescribeSpec({
  include(emailValidation)
})

val emailValidation = describeSpec {

    describe("Registration") {
        context("Not existing user") {
            forAll(
                row("test.com"),
                row("123123123123123")
            ) { email ->
                assertSoftly {
                    val exception =
                        shouldThrow<ServiceException> { Email(email) }
                }
            }
        }
    }
}

And you can parameterize the naming all you want, as that's just strings, for example:

fun emailValidation(name: String) = describeSpec {
    describe("Registration") {
        context("$name") {
        }
    }
}

If you are not parameterizing, then there's little point in having the test factory. Just declare the test inline IMO.

0
Maxim Kochetkov On

For nested include you may implement your own factory method like this example:

class FactorySpec : FreeSpec() {
    init {
        "Scenario: root container" - {
            containerTemplate()
        }
    }
}

/** Add [TestType.Container] by scope function extension */
suspend inline fun FreeScope.containerTemplate(): Unit {
    "template container with FreeScope context" - {
        testCaseTemplate()
    }
}

/** Add [TestType.Test] by scope function extension */
suspend inline fun FreeScope.testCaseTemplate(): Unit {
    "nested template testcase with FreeScope context" { }
}

Pay attention to the Scope that is passed to the extension function for containerTemplate and testCaseTemplate

Output:

Scenario: root container
   template container with FreeScope context
       nested template testcase with FreeScope context