I have a few tests which I want to run for differe...
# kotest
l
I have a few tests which I want to run for different behavior of a mocked dependency. The dependency tries to resolve a market by user location. So I have following scenarios: 1. No user location 2. User location found, but no nearby market found 3. User location found, but multiple nearby markets found Each of these scenarios will be mocked in a different way, but for each of them the test results should be the same: "no market found" should be shown. I wonder if I should use data-driven testing or just e.g. wrap the test in a
forEach
loop with lambdas containing mocks?
E.g.
Copy code
enum class TestScenario {
    NoLocation,
    NoMarket,
    MultipleMarkets,
}

fun mockMarketByLocation(scenario: TestScenario) {
    when (scenario) {
        NoLocation -> every { LocationRepository.getLocation() } returns null

        NoMarket -> {
            every { LocationRepository.getLocation() } returns mockLocation
            every { MarketRepository.getMarket(mockLocation) } returns null
        }
       // ...
    }
}

class MarketByLocationTest : StringSpec({
      
    withData(NoLocation, NoMarket, MultipleMarkets) { scenario ->
        mockMarketByLocation(scenario)
        MarketSearchViewModel().getMarket() shouldBe MarketNotFound
    }
})
a
personally I'd just write three separate tests
s
yeah or have the mock function be part of the withData
👍 1
either way
v
You can try, using nested tests Using
context
and multiple
test
blocks in it
l
This example is simplified. I also have another dependency, which has several different states to be mocked. So there would already be 5 tests for the other dependency. This multiplies by the 3 states for the dependency mocked using
withData()
.
@sam you mean like this?
Copy code
withData(
    { every { LocationRepository.getLocation() } returns null },
    {
            every { LocationRepository.getLocation() } returns mockLocation
            every { MarketRepository.getMarket(mockLocation) } returns null
    },
    // ...
) { setup ->
    setup()
    // test body
}
s
yep
can always nest withData too if want the cross product of two sets of things
l
Ok, good to know
Thank you!
l
I ended up with setting the lambdas to local variables like this:
Copy code
val noUserLocation = { coEvery { FusedLocationRepository.getUserLocation() } returns null }
and then passing a map to
withData()
Copy code
val locationSetups = mapOf(
    "no user location" to noUserLocation,
    // ...
)

withData(locationSetups) { setup ->
    setup()
    // Test body
}
a
if this is the case, maybe you should use test doubles, not mocks
l
a mock is a type of test double
You mean I should use some "fake"
FusedLocationRepository
and
MarketRepository
?
a
if your dependency is a function, not an object, you can use fake functions https://github.com/kotest/kotest/blob/master/documentation/docs/framework/fake_functions.md
l
My dependencies are objects
a
in many cases we can refactor for loose coupling. even SpringBoot lets you define your dependency as a function, and inject an implementation of a functional interface via a Bean
l
Yes, maybe in the long run it's a good approach
I'm used to working with objects
In some cases I could maybe replace them with functions
a
an once our dependency is a function, testing with fake functions becomes so very much easier
l
But some of them also hold state, so it would be harder
Yes, definitely, functional programming is much nicer
a
when used reasonably, functional programming makes our life much easier. and kotest 6 will have a couple of nice tools to help with that
👍 1
❤️ 1