Hi everyone! I am trying to initialise a `Behavior...
# kotest
a
Hi everyone! I am trying to initialise a
BehaviorSpec
. Since I need to perform some actions before the spec is initialised by using a custom Listener, but it doesn’t seem to work. Here’s an example:
Copy code
class DashboardViewModelTest : BehaviorSpec({
    listeners(
        MyListener
    )

    // [I need a listener (or multiple listeners) to be executed immediately here]

    val myClassUnderTest = MyClassUnderTest(...)

    Given("Something") {...}
    Given("Something else") {...}

    // [And then I need the same listener to de-initialise my stuff here]
})
How can I have something like that? Of course if I create a wrapping Given, for example, the listeners actually get triggered, but I was trying to avoid a further nesting level with something like Given(“My class initialised”), it seems redundant to me.
w
You could do stuff in listener’s init block perhaps? If you then created new instance of listener when registering it, you’d call whatever init block it has
a
Yes that would work, but still it seems like a hack to me. For example, some of my listeners are objects so I can’t use their init block for doing things like that; what I was wondering is if there’s an “official” way of making that code work with stock listeners, or if - for example - a main/wrapping block is always needed (which seems a bit weird to me)
also because there’s the
prepareSpec
function in
TestListener
, but it seems to never be called when my spec starts (technically it should be called as soon as the listener is set to the spec itself, which doesn’t happen and might even be a potential bug)
w
That’s what I recall, the listeners registered after the spec was already created weren’t called retroactively. I didn’t need it that much so I didn’t investigate further. Most of my listeners do things immediately though, for example:
Copy code
fun TestConfiguration.tempDir(name: String = "temp-directory"): File =
    Files.createTempDirectory(name).toFile().also { file ->
        afterSpec { file.deleteRecursively() }
    }
a
But thinking about that, by doing so, will tests running in parallel (with the multi-thread function of Kotest) be ok? 🤔
w
I don’t think why not, as the listener is created per spec anyway
👍🏻 1
a
I see, but what the listener does affects something global: it sets a property in an object, so possibly that same object’s property could be used in another thread
w
Right, that’s an issue but not really related to the original question, right? If you want parallelizable tests, you can’t use global state
a
True. Unfortunately that’s a system’s problem (I’m testing Android LiveData events, and the delegate for threading needs to be set globally for tests)
w
Yep, same here 🙂 You can define tests that aren’t parallelizable using for example tags, afaik, although I haven’t done that yet
But also unless you advance time in this view model scope, you can just set it before all tests and reset after all tests, I think, no?
I think there should be callbacks available that you can register globally, which are called around entire Kotest execution
a
mostly I think the
prepareSpec
listener method should be called immediately when the listener is attached to the spec itself, or something like that
w
Agreed, although right now it’s difficult to change due to backwards compatibility I suppose
a
true, but for example a boolean field might be added to the listener to decide if running it immediately after being attached, which might be default to
false
so to be retro-compatible. Of course then some logic would have to be added to take into account the lifecycle of the test etc
w
Frankly, I’m still not sure how such flag would work differently than for example what I pasted before.
Copy code
fun TestContext.addMyListener() {
  doStuffNow()
  afterSpec { /* cleanUp */ }