Skip to content

SameerIsmail/mockk

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

mockk

Gitter Build Status

Nice features

  • removing finals (via Java Agent)
  • 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 two installation steps.

First you need to add dependency to the library itself.

GradleMaven
testCompile "io.mockk:mockk:1.0"
    <dependency>
        <groupId>io.mockk</groupId>
        <artifactId>mockk</artifactId>
        <version>1.0</version>
        <scope>test</scope>
    </dependency>

Second you need to add class transformation agent. This agent is required to remove final modifier from running classes. There is few ways to add Java Agent/Class Loader. Only running as a Java Agent is an officially supported way. All others are existing for convenience and should be used at your own risk.

MethodInstruction
Java Agent gradle Add agent gradle plugin. Use following agent:
agent "io.mockk:mockk-agent:1.0"
Java Agent maven Add dependency to mockk-agent. Configure dependency:properties plugin run. Configure maven surefire plugin:
<argLine>-javaagent:${io.mockk:mockk-agent:jar}</argLine>
See example here
Java Agent JVM Add JVM parameter to launch agent:
-javaagent:libs/mockk-agent-1.0.jar
JUnit4 Add this dependency to your project:
testCompile "io.mockk:mockk-agent:1.0"
Use annotation for each test:
@RunWith(MockKJUnit4Runner::class)
If @RunWith is specified on superclass then it will be used to run after class loader is set. So you can specify next runner doing the real running job. Use @ChainedRunWith to override such delegated runner. If neither is specified default JUnit runner is used.
JUnit5 Just add this dependency to your project:
testCompile "io.mockk:mockk-agent:1.0"
JUnit5 test will be hooked via TestExecutionListener. Note: this implementation is totally a hack.

DSL

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)

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

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 83.5%
  • Java 15.5%
  • Other 1.0%