Skip to content

oskarbet/mockk

 
 

Repository files navigation

mockk kotlin

Gitter Build Status Relase Version Change log Back log codecov Documentation

Table of contents:

  • auto-gen TOC: {:toc}

Nice features

  • 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

Installation

There is three installation steps.

1. Add dependency

ToolInstruction
Gradle
testCompile "io.mockk:mockk:1.2"
Maven
<dependency>
    <groupId>io.mockk</groupId>
    <artifactId>mockk</artifactId>
    <version>1.2</version>
    <scope>test</scope>
</dependency>

2. Add class modification via annotation

ToolInstruction
JUnit4 Use annotation for each test:

@RunWith(MockKJUnit4Runner::class)

Use @ChainedRunWith or @RunWith on superclass to override delegated runner.

If neither is specified the default JUnit runner is used.

JUnit5

JUnit5 tests should work auto-magically.

Note: this implementation is totally a hack.

To disable class modification just create empty file resource 'io/mockk/junit/mockk-classloading-disabled.txt' on a classpath.

3. Add class modification via agent (optional)

ToolInstruction
Gradle Add agent gradle plugin.

Use following agent:

agent "io.mockk:mockk-agent:1.2"

Maven Add dependency:properties plugin.

Configure maven surefire plugin:

<argLine>-javaagent:${io.mockk:mockk-agent:jar}</argLine>

See example here

plain JVM

Add JVM parameter to launch agent(remove spaces):

-javaagent: ${HOME}/.m2/repository/ io/mockk/mockk-agent/1.2/ mockk-agent-1.2.jar

DSL examples

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) }

Partial argument matching

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) }

Chained calls

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) }

Capturing

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) }

Capturing lambda

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()) }

Capturing to the list

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) }

Verification with atLeast

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()) }

Verification sequence

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)
  }

Returning nothing

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)
  }

Coroutines

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) }

DSL tables

Here is a few tables helping to master the DSL.

Matchers

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

Validators

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

Answers

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)

Answer scope

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

Getting Help

To ask questions please use stackoverflow or gitter.

To report bugs, please use the GitHub project.

About

mocking library for Kotlin

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Kotlin 82.1%
  • Java 14.8%
  • HTML 2.5%
  • Other 0.6%