Thursday, July 28, 2022
HomeWeb DevelopmentUnit testing in Kotlin tasks with Mockk vs. Mockito

Unit testing in Kotlin tasks with Mockk vs. Mockito


Introduction

Unit testing is an age-old integral a part of the software program engineering follow. Most profitable software program merchandise use correctly written unit exams; basically, unit testing verifies the correctness of a chunk of code.

Writing code to check code could seem counterintuitive, however it’s an artwork unto itself that offers a developer confidence of their code.

Beneath are the numerous advantages we derive from writing unit exams:

  1. Catch bugs early in manufacturing code
  2. Scale back code complexity by having a unit of code doing a particular factor
  3. Function a superb supply of documentation for a developer to get perception on a undertaking
  4. Save time (and cash)
  5. Encourage writing clear and maintainable code with much less coupling

Unit testing in Kotlin tasks

The Kotlin programming language is basically executed within the JVM atmosphere. Its concise and fancy language options have made it well-liked throughout the group, and it’s getting used often in tasks corresponding to Android purposes, Kotlin Multiplatform Cell (KMM) apps, and spring boot purposes.

There are presently two well-liked frameworks constructed to help in efficient unit testing: Mockito and Mockk. On this submit, we’ll speak about every of them by way of the next sections:

Mockk vs. Mockito

Mockk and Mockito are libraries that assist write unit exams that concentrate on JVM platforms. Mockito has been round for the reason that early days of Android growth and ultimately turned the de-facto mocking library for writing unit exams.

Mockito and Mockk are written in Java and Kotlin, respectively, and since Kotlin and Java are interoperable, they’ll exist throughout the similar undertaking. Primarily, each libraries can be utilized interchangeably in these tasks, however the preeminence of Kotlin has tipped the steadiness in favor of Mockk for many Kotlin tasks.

The next factors summarize why Mockk is favored over Mockito for Kotlin tasks:

  • Top notch assist for Kotlin options
  • A pure Kotlin-mocking DSL for writing clear and idiomatic Kotlin code
  • Mocking assist for closing courses and strategies
  • Coroutine assist by default

write unit exams for Kotlin tasks

For the aim of this text, we are going to implement a easy person repository class to reveal writing unit exams in a Kotlin undertaking. There are two issues to notice earlier than we proceed:

  1. The event atmosphere can be in Android Studio
  2. We’ll examine the syntax distinction between each mocking libraries for the totally different check instances lined

Sufficient speak, allow us to get our arms soiled!


Extra nice articles from LogRocket:


Create the person repository

First, outline the interface for the repository like so:

interface UserRepository {

    droop enjoyable saveUser(person: Person)

    droop enjoyable getUser(id: String): Person

    droop enjoyable deleteUser(id: String)
}

That is principally a contract that can be carried out by the concrete class. See the code block under.

class UserRepositoryImpl constructor(
    personal val dataSource: DataSource
) : UserRepository {
    override droop enjoyable saveUser(person: Person) {
        dataSource.save(person)
    }

    override droop enjoyable getUser(id: String): Person {
        return dataSource.get(id) ?: throw IllegalArgumentException("Person with id $id not discovered")
    }

    override droop enjoyable deleteUser(id: String) {
        dataSource.clear(id)
    }
}

UserRepositoryImpl has a dependency on DataSource, by way of which it fulfills the contract by UserRepository.

DataSource is an easy Kotlin class. Its function is to retailer person knowledge in reminiscence, the place it could possibly later be retrieved. See the code block under for particulars:

class DataSource {

    personal val db = mutableMapOf<String, Person>()

    enjoyable save(person: Person) = db.let { it[user.email] = person }

    enjoyable get(key: String): Person? = db[key]

    enjoyable clear(key: String) = db.take away(key)

    enjoyable clearAll() = db.clear()
}

To maintain issues easy, I’ve used a mutableMap object to avoid wasting a Person to reminiscence. Maps are collections that holds pairs of objects (keys and values), so it is smart to have the person electronic mail function the distinctive key for saving and retrieving the Person.

<h3=”add-library-dependencies-gradle”>Add library dependencies to Gradle

Add the next dependencies to your app-level Gradle file, like so:

//Mockk
testImplementation "io.mockk:mockk:1.12.4"

//Mockito
testImplementation "org.mockito:mockito-core:4.0.0"
testImplementation "org.mockito:mockito-inline:4.0.0"
testImplementation "org.mockito.kotlin:mockito-kotlin:4.0.0"

Utilizing Mockito in a Kotlin undertaking requires some additional dependencies for the next causes:

  1. Kotlin courses are closing by default and can’t be mocked by Mockito, therefore the necessity for :mockito-inline:. You may suppose another can be so as to add the open modifier to the category concerned, however this isn’t advisable as a result of it should mess up your code base and drive you to outline your courses as open
  2. :mockito-kotlin is a library that gives useful features for working with Mockito in Kotlin tasks

Writing exams for system below check (SUT) utilizing Mockk

import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.mockk
import io.mockk.slot
import java.util.*
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.check.runTest
import org.junit.Assert
import org.junit.Check

@OptIn(ExperimentalCoroutinesApi::class)
class UserRepositoryImplTest {

    personal val dataSource = mockk<DataSource>(relaxed = true)
    personal val sut = UserRepositoryImpl(dataSource)

    @Check
    enjoyable `confirm appropriate person params are used`() = runTest {
        val person = buildUser()

        sut.saveUser(person)

        val captor = slot<Person>()

        coVerify { dataSource.save(seize(captor))}

        Assert.assertEquals(person.electronic mail, captor.captured.electronic mail)
    }

    @Check
    enjoyable `confirm appropriate person is retrieved`() = runTest {
        val electronic mail = "[email protected]"

        coEvery { dataSource.get(any()) } returns buildUser()

        val person = sut.getUser(electronic mail)

        Assert.assertEquals(electronic mail, person.electronic mail)
    }

    @Check
    enjoyable `confirm person is deleted`() = runTest {
        val electronic mail = "[email protected]"
        sut.deleteUser(electronic mail)

        coVerify { dataSource.clear(any()) }
    }

    companion object {
        enjoyable buildUser() = Person(
            id = UUID.randomUUID().toString(),
            electronic mail = "[email protected]",
            fullName = "Emmanuel Enya",
            verificationStatus = Person.VerificationStatus.Verified,
            memberShipStatus = Person.MemberShipStatus.Free
        )
    }
}

The above code block is a reasonably easy check class with minimal check instances. On the high degree of the category physique, I’ve mocked the info supply and created an occasion of the system below check.

Discover that DataSource is mocked with chill out set to true, like so:

mockk<DataSource>(relaxed = true)

This type of mock returns a easy worth for all features, permitting you to skip specifying conduct for every case. Verify the Mockk documentation for extra particulars on relaxed mocks.

Different sections of the code block can be examined side-by-side with the Mockito variant of the check class.

Writing exams for system below check (SUT) utilizing Mockito

import java.util.*
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.check.runTest
import org.junit.Assert
import org.junit.Check
import org.mockito.Mockito.*
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor

@OptIn(ExperimentalCoroutinesApi::class)
class UserRepositoryImplTestMockito {

    personal val dataSource = mock(DataSource::class.java)
    personal val sut = UserRepositoryImpl(dataSource)

    @Check
    enjoyable `confirm appropriate person params are used`() = runTest {
        val person = buildUser()

        sut.saveUser(person)

        val captor = argumentCaptor<Person>()

        confirm(dataSource).save(captor.seize())

        Assert.assertEquals(person.electronic mail, captor.firstValue.electronic mail)
    }

    @Check
    enjoyable `confirm appropriate person is retrieved`() = runTest {
        val electronic mail = "[email protected]"

        `when`(dataSource.get(any())).then { buildUser() }

        val person = sut.getUser(electronic mail)

        Assert.assertEquals(electronic mail, person.electronic mail)
    }

    @Check
    enjoyable `confirm person is deleted`() = runTest {
        val electronic mail = "[email protected]"
        sut.deleteUser(electronic mail)

        confirm(dataSource).clear(any())
    }


    companion object {

        enjoyable buildUser() = Person(
            id = UUID.randomUUID().toString(),
            electronic mail = "[email protected]",
            fullName = "Emmanuel Enya",
            verificationStatus = Person.VerificationStatus.Verified,
            memberShipStatus = Person.MemberShipStatus.Free
        )
    }
}

The above code block is a check class written with Mockito. On the high degree of the category physique, we now have the mocked dependency and the SUT arrange similarly to how we did with Mockk.

Nonetheless, one salient level to notice is that there is no such thing as a relaxed mock argument. The rationale for it is because Mockito supplies default solutions for behaviors when they aren’t stubbed.

Conclusions

I really feel Mockk shines right here as a result of mocks usually are not relaxed by default, which inspires you to have whole management over the features name being made by the SUT.

Argument capturing in Mockk and Mockito

argumentCaptor is a operate from the mockito-kotlin extension library. It helps to seize a single argument from the mocked object, normally achieved within the verification block.

The Mockk variant is a slot.

Stubbing

Normally, when writing unit exams, we specify solutions to operate calls on mocked objects. That is known as stubbing in unit testing.

Utilizing Mockito, it’s declared like so:

`when`(dataSource.get(any())).then { buildUser() }

Mockito doesn’t have inbuilt assist for Kotlin coroutines, which implies testing coroutines would require the usage of runTest for the code to compile.

In Mockk, there’s coEvery { } for stubbing coroutines and each { } for normal features. This distinction provides readability to the code being examined.

Verification

An vital a part of unit testing is to confirm the tactic interactions of mocked objects in manufacturing code.
droop features can solely be invoked by different droop features. Having coVerify{ } in place provides builders the assist to stub droop features, which ordinarily wouldn’t be attainable with the confirm{ } block.

Mockk supplies two features to check droop features and common features:

  1. coVerify { }, for droop features
  2. confirm { }, for normal features

Mockito makes use of confirm() for verification and nonetheless requires utilizing runTest to assist droop features. This nonetheless works, however is much less clear than Mockk’s strategy.

Conclusion

We’ve explored unit testing in Kotlin tasks and how one can successfully write exams with Mockk and Mockito.

Be at liberty to make use of whichever library you discover enticing, however I might suggest Mockk due to how versatile it’s when working with the Kotlin language.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

- Advertisment -
Google search engine

Most Popular

Recent Comments