Karmok

Mock generator and library

License

License

GroupId

GroupId

co.touchlab
ArtifactId

ArtifactId

karmok-library
Last Version

Last Version

0.1.8
Release Date

Release Date

Type

Type

pom
Description

Description

Karmok
Mock generator and library
Project URL

Project URL

https://github.com/touchlab/Karmok
Source Code Management

Source Code Management

https://github.com/touchlab/Karmok

Download karmok-library

How to add to project

<!-- https://jarcasting.com/artifacts/co.touchlab/karmok-library/ -->
<dependency>
    <groupId>co.touchlab</groupId>
    <artifactId>karmok-library</artifactId>
    <version>0.1.8</version>
    <type>pom</type>
</dependency>
// https://jarcasting.com/artifacts/co.touchlab/karmok-library/
implementation 'co.touchlab:karmok-library:0.1.8'
// https://jarcasting.com/artifacts/co.touchlab/karmok-library/
implementation ("co.touchlab:karmok-library:0.1.8")
'co.touchlab:karmok-library:pom:0.1.8'
<dependency org="co.touchlab" name="karmok-library" rev="0.1.8">
  <artifact name="karmok-library" type="pom" />
</dependency>
@Grapes(
@Grab(group='co.touchlab', module='karmok-library', version='0.1.8')
)
libraryDependencies += "co.touchlab" % "karmok-library" % "0.1.8"
[co.touchlab/karmok-library "0.1.8"]

Dependencies

There are no dependencies for this project. It is a standalone project that does not depend on any other jars.

Project Modules

There are no modules declared in this project.

Karmock

Karmok consists of an intelliJ plugin and a supporting library dependency which can help you manage interfaces in your unit tests for a KMP project. It is currently experimental and is not our vision for a complete KMP mocking solution, but it is definitely helpful for now.

Karmok allows you to generate a mock implementation of an interface which is capable of verifying method calls and overriding their results.

Pre-alpha

Karmock is a library used by Touchlab as an experimental tool. It's meant as an internal tool, as well as an experiment to find out what could go into a full fledged Kotlin Multiplatform mocking solution. We are simply making it public to show how a kmp mocking solution could work. If you decide to use Karmock, know that Touchlab is not providing any technical support for this library.

Limitations

Currently Karmock has a few limitations which should be mentioned here:

  • There is no concept of spies in Karmok.
  • The library only supports Interfaces
  • The IDE plugin is not in the marketplace, so you get it from the release section or build it yourself
  • Boilerplate is added to your code by the plugin when creating a mock

There is no guarantee that these limitations will change, as mentioned earlier this is an pre-alpha experimental tool

Installation

karmok-library

The library includes all the plumbing code which performs the recording and configuration of your mocks. Code generated by the plugin will expect this library to be present.

implementation(“co.touchlab:karmok-library:0.1.7”)

idea-plugin

This is the plugin which will generate mock implementations for you . To test, go to 'Tasks > intellij > runIde'. You'll need to create a project, or import an existing one. You'll need to add the mavenLocal repo and add the library dependency to use the generated mocks.

To use the plugin, create a class that implements an interface. Option+Enter on the class name and you should see "Implement Mock Members". Select the members you want to include (which should be all, but still).

pop up

You can import the plugin using the zip from the releases section on GitHub, using intellij's settings pane. Alternatively you can build the plugin with 'buildPlugin', which makes a zip in 'build/distributions'.

import

Usage

Say we want to create a mock implementation for the following interface:

interface MyInterface {
    val name: String
    fun hey(): Long
}

The first step will be to create an empty class with the interface in it’s signature

class MockMyInterface: MyInterface {

}

The IDE will complain that MockMyInterface doesn’t implement the necessary methods to fulfill MyInterface. Rather than write them ourselves, we will use the Karmok plugin to fill in the class. Press Option+Enter on the class name to open the quick fix dialog. In the list you should see “Implement Mock Members”. Select all the members and press ok to generate the body The intellij plugin will add implementation inside TestingImpl

class MockMyInterface: MyInterface {
    internal val mock = InnerMock()
    override val name: String by mock.name
    override fun hey(): Long {
        return mock.hey.invoke({ hey() }, listOf())
    }

    class InnerMock(delegate: Any? = null) : MockManager(delegate) {
        internal val name = MockPropertyRecorder<TT, String>({ name }) {}
        internal val hey = MockFunctionRecorder<TT, Long>()
    }
}

The implementation includes an inner class called InnerMock, which extends MockManager. Each function and property of the interface gets a Recorder instance in InnerMock. These classes are included in the karmok-library dependency which must be present. The recorders can be configured with what to be returned when called, and can be verified after you are done with interaction.

@Test
fun helloTest(){
    val tt = MockMyInterface()
    tt.mock.name.returnOnCall("touchlab")
    tt.mock.hey.returns(22L)
    
    assertEquals(tt.hey(), 22L)
}

There are 2 recorders, MockFunctionRecorder and MockPropertyRecorder. I've added a set of interfaces for each that are focused on config and verify operations. For example, for properties:

interface MockPropertyConfigure<RT> {
    fun throwOnCall(t: Throwable)
    fun returnOnCall(rt: RT)
}

interface MockPropertyVerify<RT> {
    val getCalled: Boolean
    fun setCalled(rt: RT): Boolean
    val calledCountGet: Int
    val calledCountSet: Int
}

These are separate because the next goal was to improve the config and verify steps, but that is incomplete. It would look something like the following:

@Test
fun helloTest(){
    val tt = MockMyInterface()
    tt.mock.config {
        name.returnOnCall("touchlab")
        hey.returns(22L)
    }
    
    assertEquals(tt.hey(), 22L)
    
    tt.mock.verify {
        assertEquals(hey.calledCount, 1)
    }
}

Not implemented, but that's why there are multiple interfaces. Currently, the config and verify functions can feel a little confusing because they both show up on autocomplete, but you'll probably get used to it.

Issues

Currently the plugin does not add the imports for the inserted plumbing code. This is technically not too difficult, but it's also not super critical. To add later.

co.touchlab

Touchlab

Versions

Version
0.1.8
0.1.7-1.4.0-rc
0.1.7-1.4-M3
0.1.7-1.4-M2
0.1.7
0.1.6