https://kotlinlang.org logo
Title
f

Filip Piechowski

04/13/2022, 9:40 AM
Hi. I have a problem i’m struggling with for a few days. I want to create a DSL-like
TestFactory
builder, that would nest further steps declared by user in a lambda as the last parameter of my test factory. In my specific case that would look like this:
class MySpec : FreeSpec({
    include(replicatorSpec(...) { // this: ReplicatorTestFactoryConfiguration extending TestFactoryConfiguration(), FreeSpecRootScope
        parent(...) { // this: ReplicatorTestFactoryConfiguration.AfterParent extending TestFactoryConfiguration(), FreeSpecRootScope
            create(...) { // this: ReplicatorTestFactoryConfiguration.AfterCreate extending TestFactoryConfiguration(), FreeSpecRootScope
                update(...)
            }
        }
    )}
})

inline fun RootScope.replicatorSpec(
    ...,
    crossinline configure: ReplicatorTestFactoryConfiguration.() -> Unit = { }
) = freeSpec {
    <http://logger.info|logger.info> { "replicatorSpec" }

    beforeSpec {...}

    val configuration = ReplicatorTestFactoryConfiguration(...)

    configuration.configure()

    afterSpec {...}
}

inline fun ReplicatorTestFactoryConfiguration.parent(
    ... ,
    crossinline then: ReplicatorTestFactoryConfiguration.AfterParent<P>.() -> Unit = {}
) { ... }

inline fun ReplicatorTestFactoryConfiguration.AfterParent.create(
    ... ,
    crossinline then: ReplicatorTestFactoryConfiguration.AfterParent<P>.AfterCreate<P, COUT>.() -> Unit = { }
) { ... }

inline fun ReplicatorTestFactoryConfiguration.AfterParent.AfterCreate.update(...) { ... }
But when running tests and putting debug FreeSpec test scopes (
"debug" - { }
) i see that execution gets only to the
replicatorSpec(...)
and the FreeSpecScopes defined in there, but anything further like the code defined in lambda. How can I create such a
s

sam

04/13/2022, 9:49 AM
This is pretty deep use of the builders 🙂
f

Filip Piechowski

04/13/2022, 9:54 AM
yeah, the thing is each fuirther step is dependant on the result of the previous step, eg. update depends on the output of create
s

sam

04/13/2022, 10:02 AM
This works, I hope it helps:
class WildFactories : FreeSpec() {
   init {
      include(replicatorSpec {
         parent {
            "foo1" - {
               "bar1" {
                  1 shouldBe 1
               }
            }
         }
      })

      "foo2" - {
         "bar2" {
            1 shouldBe 1
         }
      }
   }
}

class ReplicatorTestFactoryConfiguration(val c: FreeSpecTestFactoryConfiguration) : TestFactoryConfiguration(),
   FreeSpecRootScope

fun replicatorSpec(configure: ReplicatorTestFactoryConfiguration.() -> Unit) = freeSpec {
   val configuration = ReplicatorTestFactoryConfiguration(this)
   configuration.configure()
}

fun ReplicatorTestFactoryConfiguration.parent(context: FreeSpecTestFactoryConfiguration.() -> Unit) {
   this.c.context()
}
f

Filip Piechowski

04/13/2022, 12:26 PM
@sam Your way forces the parameter to be of type
FreeSpecTestFactoryConfiguration.() -> Unit
while I need it to be
ReplicatorTestFactoryConfiguration
because i store state in this class. eventually i can store the stare in parameters inside
( … )
but it look cleaner with it sitting in
this
context
s

sam

04/13/2022, 12:31 PM
You can play with moving the function part of it inside
I just wanted to get you started on something that worked