I have a Kotlin code like this. Using Mockk for testing.
class BugRepository {
fun fetchAll(ids: List<Long>): List<Bug> { ... some fetching code ... }
}
Now I have BugService
using that repository and I have BugServiceTest
that uses code like this:
val mocks = listOf(Bug(...), Bug(...))
every { bugRepository.fetchAll(mocks.map { it.id }) } returns mocks
How could I improve that, so the order of the ids passed to fetchAll
method does not matter? Is there any mockk matcher for that?
Alas, the up to now accepted answer is not completely correct.
Suppose you have
val mocks = listOf(Bug(1), Bug(2))
The matcher in the solution does accept every map that contains all id
s of mocks
, but it is not at all a problem if there are even some more elements. That means, that the every
clause is matched for listOf(1, 2)
and listOf(2, 1)
, as it should, but also for for listOf(1, 2, 3)
.
If you can be sure that there are no duplicates in mocks
and the supplied list in the test, the matcher can simply be improved using equality of sets:
every {
bugRepository.fetchAll(
match { list -> list.toSet() == mocks.map { it.id }.toSet() }
)
} returns mocks
But this will not work, if there are any duplicates, e.g. listOf(Bug(1), Bug(1))
will be regarded equal to listOf(Bug(1))
.
If you want to take into account also the existence of duplicates, that can be done with a custom matching function that groups all duplicates and compares these in the two lists:
fun <T> List<T>.containsExactlyInAnyOrder(list: List<T>): Boolean {
val duplicates = groupBy { it }
val listDuplicates = list.groupBy { it }
return duplicates.size == listDuplicates.size &&
duplicates.all { listDuplicates[it.key] == it.value }
}
Then you can use this inside your every
clause:
every {
bugRepository.fetchAll(
match { list -> list.containsExactlyInAnyOrder(mocks.map { it.id }) }
)
} returns mocks