Matchers
Please read matchers limitations section!
Regular matchers
Matchers are quite straightforward to use. Instead of literal argument, you have to pass a matcher. You can use named parameters and change their order. Mixing matchers and literal arguments is also allowed.
Full list of matchers with documentation is available here.
Logical matchers
Logical matchers allows combining regular matchers into logical expressions.
everySuspend { repository.findById(or(eq("1"), eq("2"))) } returns stubBook()
Full list of logical matchers is available here.
Vararg matchers
To match a method with varargs you can use regular matchers:
everySuspend { repository.findAllById("1", any(), "3") } returns emptyList()
The problem with regular matchers here is that the number of varargs is always fixed. Answer definition above works only
for calls with "1"
at index 0, any arg at index 1 and "3"
at index 2.
To solve this problem you can use wildcard matchers:
everySuspend { repository.findAllById("1", *anyVarargs(), "3") } returns emptyList()
Now all findAllById
calls with "1"
as the first argument and "3"
as the last argument return an empty list.
You can apply restrictions with wildcard matchers using varargsAny
and varargsAll
:
everySuspend { repository.findAllById("1", *varargsAll { it != "2" }, "3") } returns emptyList()
repository.findAllById("1", "3", "3", "3") // returns empty list
repository.findAllById("1", "2", "3", "3") // fails - method not mocked
Wildcard vararg matchers can be used with composite matchers (e.g. logical matchers):
everySuspend { repository.findAllById(*anyVarargs()) } returns listOf(stubBook("1"))
everySuspend { repository.findAllById(*not(varargsAll { it == "2" })) } returns listOf(stubBook("2"))
repository.findAllById("2", "2", "2") // returns listOf(stubBook("1"))
repository.findAllById("1", "2", "2") // returns listOf(stubBook("2"))
Custom matchers
The most straightforward way to define a custom matcher is by defining an extension on ArgMatchersScope
:
// only for strings
fun ArgMatchersScope.regex(
regex: Regex
): String = matching(toString = { "regex($regex)" }, predicate = regex::matches)
// for any type
inline fun <reified T> ArgMatchersScope.eqAnyOf(
vararg values: T
): T = matching(
toString = { "eqAnyOf(${values.contentToString()})" },
predicate = { values.contains(it) }
)
It is possible to use matching
as anonymous matcher directly.
You can also implement ArgMatcher and pass its instance as an argument to ArgMatchersScope.matches method.
Arguments capturing
Arguments capturing allows accessing arguments passed to mocks:
val slot = Capture.slot<String>() // stores only the latest value
everySuspend { repository.findById(capture(slot)) } returns stubBook()
repository.findById("1")
println(slot.get()) // prints "1"
By default capture
matches any argument. You can change it by providing a different matcher:
val slot = Capture.slot<String>()
everySuspend { repository.findById(capture(slot, neq("1"))) } returns stubBook()
repository.findById("2")
println(slot.get()) // prints "2"
repository.findById("1") // fails - no answer provided for arg "1"
Argument capture occurs only if given definition is actually used to provide an answer for a call:
val container = Capture.container<String>() // stores multiple values
everySuspend { repository.findByName(query = any(), limit = any()) } returns listOf(stubBook())
everySuspend { repository.findByName(query = capture(container), limit = eq(10)) } returns listOf(stubBook())
everySuspend { repository.findByName(query = eq("Book 3"), limit = any()) } returns listOf(stubBook())
repository.findByName(query = "Book 1", limit = 10) // `query` arg is captured
repository.findByName(query = "Book 2", limit = 20) // `limit` parameter does not match - argument is not captured
repository.findByName(query = "Book 3", limit = 10) // answer defined later is selected here - argument is not captured
println(container.values) // prints ["Book 1"]
Argument capturing works with vararg matchers (including wildcard matchers):
val slot = Capture.slot<Array<String>>()
everySuspend { repository.findAllById(*capture(slot, anyVarargs())) } returns listOf(stubBook("1"))
repository.findAllById("1", "2", "3")
// slot contains arrayOf("1", "2", "3")
For positional vararg matchers syntax is exactly the same as for regular matchers.