Table of contents:
- auto-gen TOC: {:toc}
- removing finals (via runtime class modification)
- pure kotlin mocking DSL
- partial arguments matchers
- chained calls / deep stubs
- matcher expressions
- mocking coroutines
- capturing lambdas
- bunch of matchers
- few verification modes
There is three installation steps.
Tool | Instruction |
---|---|
testCompile "io.mockk:mockk:1.2" |
|
<dependency> <groupId>io.mockk</groupId> <artifactId>mockk</artifactId> <version>1.2</version> <scope>test</scope> </dependency> |
Tool | Instruction |
---|---|
Add agent gradle plugin.
Use following agent:
|
|
Add dependency:properties plugin.
Configure maven surefire plugin:
See example here |
|
Add JVM parameter to launch agent(remove spaces):
|
Simplest example:
val car = mockk<Car>()
every { car.drive(Direction.NORTH) } returns Outcome.OK
car.drive(Direction.NORTH) // returns OK
verify { car.drive(Direction.NORTH) }
You can skip parameters while specifying matchers. MockK runs your block few times, builds so called signature and auto-detects places where matchers appear:
class MockedClass {
fun sum(a: Int, b: Int) = a + b
}
val obj = mockk<MockedClass>()
every { obj.sum(1, eq(2)) } returns 5
obj.sum(1, 2) // returns 5
verify { obj.sum(eq(1), 2) }
Mock can have child mocks. This allows to mock chains of calls:
class MockedClass1 {
fun op1(a: Int, b: Int) = a + b
}
class MockedClass2 {
fun op2(c: Int, d: Int): MockedClass1 = ...
}
val obj = mockk<MockedClass2>()
every { obj.op2(1, eq(2)).op1(3, any()) } returns 5
obj.op2(1, 2) // returns child mock
obj.op2(1, 2).op1(3, 22) // returns 5
verify { obj.op2(1, 2).op1(3, 22) }
Simplest way of capturing is capturing to the CapturingSlot
:
class MockedClass {
fun sum(a: Int, b: Int) = a + b
}
val obj = mockk<MockedClass>()
val slot = slot<Int>()
every { obj.sum(1, capture(slot)) } answers { 2 + slot.captured!! }
obj.sum(1, 2) // returns 4
verify { obj.sum(1, 2) }
You can capture lambdas with CapturingSlot<Any>
,
but for convenience there is captureLambda construct present:
class MockedClass {
fun sum(a: Int, b: () -> Int) = a + b()
}
val obj = mockk<MockedClass>()
every {
obj.sum(1, captureLambda(Function0::class))
} answers {
2 + lambda.invoke<Int>()!!
}
obj.sum(1) { 2 } // returns 4
verify { obj.sum(1, any()) }
If you need several captured values you can capture values to the MutableList
.
Method captured()
is returning the last element of the list.
class MockedClass {
fun sum(a: Int, b: Int) = a + b
}
val obj = mockk<MockedClass>()
val lst = mutableListOf<Int>()
every { obj.sum(1, capture(lst)) } answers { 2 + lst.captured() }
obj.sum(1, 2) // returns 4
verify { obj.sum(1, 2) }
Checking at least how much method was called:
class MockedClass {
fun sum(a: Int, b: Int) = a + b
}
val obj = mockk<MockedClass>()
val lst = mutableListOf<Int>()
every {
obj.sum(any(), capture(lst))
} answers {
1 + firstArg<Int>() + lst.captured()
}
obj.sum(1, 2) // returns 4
obj.sum(1, 3) // returns 5
obj.sum(2, 2) // returns 5
verify(atLeast=3) { obj.sum(any(), any()) }
Checking the exact sequence of calls:
class MockedClass {
fun sum(a: Int, b: Int) = a + b
}
val obj = mockk<MockedClass>()
val slot = slot<Int>()
every {
obj.sum(any(), capture(slot))
} answers {
1 + firstArg<Int>() + slot.captured!!
}
obj.sum(1, 2) // returns 4
obj.sum(1, 3) // returns 5
obj.sum(2, 2) // returns 5
verifySequence {
obj.sum(1, 2)
obj.sum(1, 3)
obj.sum(2, 2)
}
If the method is returning Unit(i.e. no return value) you still need to specify return value:
class MockedClass {
fun sum(a: Int, b: Int): Unit {
println(a + b)
}
}
val obj = mockk<MockedClass>()
every { obj.sum(any(), 1) } answers { nothing }
every { obj.sum(any(), 2) } returns null
obj.sum(1, 1)
obj.sum(1, 2)
verify {
obj.sum(1, 1)
obj.sum(1, 2)
}
To mock coroutines you need to add dependency to the support library.
Gradle |
---|
testCompile "org.jetbrains.kotlinx:kotlinx-coroutines-core:x.x" |
Maven |
---|
<dependency> <groupId>org.jetbrains.kotlinx</groupId> <artifactId>kotlinx-coroutines-core</artifactId> <version>x.x</version> <scope>test</scope> </dependency> |
Then you can use coEvery
and coVerify
versions to mock coroutine methods
val car = mockk<Car>()
coEvery { car.drive(Direction.NORTH) } returns Outcome.OK
car.drive(Direction.NORTH) // returns OK
coVerify { car.drive(Direction.NORTH) }
Here is a few tables helping to master the DSL.
By default simple arguments are matched using eq()
Matcher | Description |
---|---|
any() |
matches any argument |
allAny() |
special matcher that uses any() instead of eq() for matchers that are provided as simple arguments |
isNull() |
checks if values is null |
isNull(inverse=true) |
checks if values is not null |
ofType(type) |
checks if values belongs to the type |
match { it.startsWith("string") } |
matches via arbitary lambda expression |
eq(value) |
matches if value is equal to the provided via deepEquals method |
refEq(value) |
matches if value is equal to the provided via reference comparation |
cmpEq(value) |
matches if value is equal to the provided via compareTo method |
less(value) |
matches if value is less to the provided via compareTo method |
more(value) |
matches if value is more to the provided via compareTo method |
less(value, andEquals=true) |
matches if value is less or equals to the provided via compareTo method |
more(value, andEquals=true) |
matches if value is more or equals to the provided via compareTo method |
and(left, right) |
combines two matchers via logical and |
or(left, right) |
combines two matchers via logical or |
not(matcher) |
negates the matcher |
capture(slot) |
captures a value to a CapturingSlot |
capture(mutableList) |
captures a value to a list |
captureNullable(mutableList) |
captures a value to a list together with null values |
captureLambda(lambdaClass) |
captures lambda expression(allowed one per call) |
invoke(args) |
calls matched as lambda |
hint(cls) |
hints next return type in case it's got erased |
Few special matchers available in verification mode only:
Matcher | Description |
---|---|
any { code } |
matches any argument and allows to execute some code |
assert { predicate } |
matches any argument, checks if assertions is true |
Validator | Description |
---|---|
verify { mock.call() } |
Do unordered verification that call were performed |
verify(inverse=true) { mock.call() } |
Do unordered verification that call were not performed |
verify(atLeast=n) { mock.call() } |
Do unordered verification that call were performed at least n times |
verify(atMost=n) { mock.call() } |
Do unordered verification that call were performed at most n times |
verify(excatly=n) { mock.call() } |
Do unordered verification that call were performed at exactly n times |
verifyOrder { mock.call1(); mock.call2() } |
Do verification that sequence of calls went one after another |
verifySequence { mock.call1(); mock.call2() } |
Do verification that only the specified sequence of calls were executed for mentioned mocks |
Answer | Description |
---|---|
returns value |
specify that matched call returns one specified value |
returnsMany list |
specify that matched call returns value from the list, returning each time next element |
throws ex |
specify that matched call throws an exception |
answers { code } |
specify that matched call answers with lambda in answer scope |
answers answerObj |
specify that matched call answers with Answer object |
answers { nothing } |
specify that matched call answers null |
just Runs |
specify that matched call is returning Unit (returns null) |
Parameter | Description |
---|---|
call |
a call object that consists of invocation and matcher |
invocation |
contains information regarding actual method invoked |
matcher |
contains information regarding matcher used to match invocation |
self |
reference the object invocation made |
method |
reference to the method invocation made |
args |
reference to arguments of invocation |
nArgs |
number of invocation argument |
firstArg() |
first argument |
secondArg() |
second argument |
thirdArg() |
third argument |
lastArg() |
last argument |
captured() |
the last element in the list for convenience when capturing to the list |
lambda |
captured lambda |
nothing |
null value for returning nothing as an answer |
To ask questions please use stackoverflow or gitter.
- Chat/Gitter: https://gitter.im/mockk-io/Lobby
- Stack Overflow: http://stackoverflow.com/questions/tagged/mockk
To report bugs, please use the GitHub project.
- Project Page: https://github.com/oleksiyp/mockk
- Reporting Bugs: https://github.com/oleksiyp/mockk/issues