https://kotlinlang.org logo
#kotest
Title
i

Ioannis Mavroukakis

09/19/2022, 4:47 PM
Hello folks! I have just started tinkering with
kotest
and I am happy with it thus far, except for one little thing that I cannot wrap my head around.
Copy code
class DummySpec : FreeSpec(
    {
        val mockedBeeper = mockk<Beeper>()

        beforeTest {
            clearAllMocks()
            println("mock cleared")
        }

        "beeper will beep" - {
            "when the stars have aligned" - {
                every { mockedBeeper.beep(any()) } returns "mocked beep"
                "and the moon is high" {
                    mockedBeeper.beep(true) shouldBe "mocked beep"
                }
            }
        }
    },
)

class Beeper {
    fun beep(shouldBeep: Boolean): String = if (shouldBeep) {
        "BEEP"
    } else "shhhhh"
}
this will clear the
mock
three times, once for each line in the test spec. Is there anyway to use
beforeTest
but limit it to be executed only for the top test method? Thanks!
pretend there are more test sections in this Spec 😉
this is not limited to
FreeSpec
of course, it also applies to the BDD-style spec..
l

LeoColman

09/19/2022, 5:12 PM
Take a look at IsolationModes
Maybe that's the kind of thing you want to tweak, the isolation between test scopes
i

Ioannis Mavroukakis

09/19/2022, 5:13 PM
I have, doesn't do what I want
The problem is that
beforeTest
is executed for every test description 😞
l

LeoColman

09/19/2022, 5:14 PM
Maybe
beforeContainer
or
beforeSpec
?
Or even clearing mocks after the tests, ordering might play a big role for your case hahah
i

Ioannis Mavroukakis

09/19/2022, 5:15 PM
beforeSpec is too coarse grained, beforeContainer behaves in the same way 😞
essentially I need something that says, • for each "top level" test description • run
beforeTest
once • and ignore other nested descriptions
makes sense?
l

LeoColman

09/19/2022, 5:17 PM
Yes. Could you give more details in your use case? What is your test flow, what are you trying to accomplish?
What test you expect to pass but isn't passing sort of speak
i

Ioannis Mavroukakis

09/19/2022, 5:18 PM
basically
mockk
is complaining because
"and the moon is high"
runs
beforeTest
again and splats the mocked object setup
instead, what I want it to do is run
beforeTest
just once , for
when the stars have aligned
and I don't think there is a way to do this.
maybe using
clearAllMocks()
doesn't make sense here and I need to create them as `val`s
rubber duck in progress
t

thanksforallthefish

09/20/2022, 6:18 AM
you can use
clearMockks(answer = false)
, but in general I agree that it seems to me there is something wrong with your setup
i

Ioannis Mavroukakis

09/20/2022, 8:04 AM
@thanksforallthefish thanks, wrong in what sense though?
it seems to me that for BDD style or FreeSpec tests there should be a way to run
beforeTest
just once, not once for every nesting level.
t

thanksforallthefish

09/20/2022, 8:06 AM
to me it looks weird you want to clean up stuff before the first test only, it (probably) means somewhere else you have a test that leaves things behind. I think before should be for setup, and after for general verifications and clean up, so seeing a clean up before a test is always weird
i

Ioannis Mavroukakis

09/20/2022, 8:08 AM
hmm perhaps I am not explaining properly..
what I am saying is, that between
"beeper will beep"
,
"when the stars have aligned"
and
"and the moon is high"
,
beforeTest
gets invoked every time. If this is by design, fine, but it means that any
mockk
setup in-between them, will get reset
upshot is, you can't be expressive and layer your mocks, everything has to go to the innermost context.
yeah, I agree that
clearAlllMocks
between tests might be wrong in the first place but when you are translating JUnit based tests to the FreeSpec or BDD styles, it's a weird little gotcha.
t

thanksforallthefish

09/20/2022, 8:50 AM
something like
Copy code
class DummySpec : FreeSpec(
    {
        val mockedBeeper = mockk<Beeper>()

        "beeper will beep" - {
            clearAllMocks()
            println("mock cleared")
            "when the stars have aligned" - {
                every { mockedBeeper.beep(any()) } returns "mocked beep"
                "and the moon is high" {
                    mockedBeeper.beep(true) shouldBe "mocked beep"
                }
            }
        }
    },
)

class Beeper {
    fun beep(shouldBeep: Boolean): String = if (shouldBeep) {
        "BEEP"
    } else "shhhhh"
}
also clears only once, but ofc does not scale if you have many top level “tests” in your spec. maybe you should really look at isolation, there might something there. I never fully understood it and felt it opens up for bad practices (somehow we have many many tests all works with default isolation, but we are also not using “crazy” set ups and prefer duplicating stuff in tests to keep them simple)
i

Ioannis Mavroukakis

09/20/2022, 8:51 AM
That was one of my considerations, thanks Marco.
3 Views