Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mock a Kotlin singleton object?

Given a Kotlin singleton object and a fun that call it's method

object SomeObject {    fun someFun() {} }  fun callerFun() {    SomeObject.someFun() } 

Is there a way to mock call to SomeObject.someFun()?

like image 933
user3284037 Avatar asked Jun 22 '16 20:06

user3284037


People also ask

Can Mockito mock singleton?

If you are using Mockito 3.4. 0+, you may mock a singleton like the following, without PowerMock or other dependencies.

How do I get singleton object in Kotlin?

Using Singletons in Kotlin. By using the keyword object in your app, you're defining a singleton. A singleton is a design pattern in which a given class has only one single instance inside the entire app.

How do you initialize singleton in Kotlin?

But in Kotlin, we need to use the object keyword. The object class can have functions, properties, and the init method. The constructor method is not allowed in an object so we can use the init method if some initialization is required and the object can be defined inside a class.


2 Answers

There's a very nice mocking library for Kotlin - Mockk, which allows you to mock objects, the exact same way you're desiring.

As of its documentation:


Objects can be transformed to mocks following way:

object MockObj {   fun add(a: Int, b: Int) = a + b }  mockkObject(MockObj) // aplies mocking to an Object  assertEquals(3, MockObj.add(1, 2))  every { MockObj.add(1, 2) } returns 55  assertEquals(55, MockObj.add(1, 2)) 

To revert back use unmockkAll or unmockkObject:

@Before fun beforeTests() {     mockkObject(MockObj)     every { MockObj.add(1,2) } returns 55 }  @Test fun willUseMockBehaviour() {     assertEquals(55, MockObj.add(1,2)) }  @After fun afterTests() {     unmockkAll()     // or unmockkObject(MockObj) } 

Despite Kotlin language limits you can create new instances of objects if testing logic needs that:

val newObjectMock = mockk<MockObj>() 
like image 174
LeoColman Avatar answered Sep 22 '22 17:09

LeoColman


Just make you object implement an interface, than you can mock you object with any mocking library. Here example of Junit + Mockito + Mockito-Kotlin:

import com.nhaarman.mockito_kotlin.mock import com.nhaarman.mockito_kotlin.whenever import org.junit.Assert.assertEquals import org.junit.Test  object SomeObject : SomeInterface {     override fun someFun():String {         return ""     } }  interface SomeInterface {     fun someFun():String }  class SampleTest {      @Test     fun test_with_mock() {         val mock = mock<SomeInterface>()          whenever(mock.someFun()).thenReturn("42")          val answer = mock.someFun()          assertEquals("42", answer)     } } 

Or in case if you want mock SomeObject inside callerFun:

import com.nhaarman.mockito_kotlin.mock import com.nhaarman.mockito_kotlin.whenever import org.junit.Assert.assertEquals import org.junit.Test  object SomeObject : SomeInterface {     override fun someFun():String {         return ""     } }  class Caller(val someInterface: SomeInterface) {     fun callerFun():String {         return "Test ${someInterface.someFun()}"     } }  // Example of use val test = Caller(SomeObject).callerFun()  interface SomeInterface {     fun someFun():String }  class SampleTest {      @Test     fun test_with_mock() {         val mock = mock<SomeInterface>()         val caller = Caller(mock)          whenever(mock.someFun()).thenReturn("42")          val answer = caller.callerFun()          assertEquals("Test 42", answer)     } } 
like image 37
Ruslan Avatar answered Sep 19 '22 17:09

Ruslan