如何模拟Kotlin单身物件?

给定一个Kotlin单身对象和一个叫它的方法的乐趣

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

有没有办法模拟调用SomeObject.someFun()

只是让你的对象实现一个接口,比你可以嘲笑你反对任何嘲笑库。 这里是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() whenever(mock.someFun()).thenReturn("42") val answer = mock.someFun() assertEquals("42", answer) } } 

或者,如果你想在模拟SomeObject内部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() val caller = Caller(mock) whenever(mock.someFun()).thenReturn("42") val answer = caller.callerFun() assertEquals("Test 42", answer) } } 

您可以使用类委托来嘲笑Object,而无需使用任何额外的库。

这是我的建议

 val someObjectDelegate : SomeInterface? = null object SomeObject: by someObjectDelegate ?: SomeObjectImpl object SomeObjectImpl : SomeInterface { fun someFun() { println("SomeObjectImpl someFun called") } } interface SomeInterface { fun someFun() } 

在您的测试中,您可以设置将改变行为的委托对象,否则将使用它的真正的实现。

 @Beofre fun setUp() { someObjectDelegate = object : SomeInterface { fun someFun() { println("Mocked function") } } // Will call method from your delegate SomeObject.someFun() } 

当然上面的名字是不好的,但是为了举例说明它的目的。

SomeObject初始化后,委托将处理所有的function。
更多你可以在官方文档中find

Kotlin有一个非常好的新嘲笑图书馆。 这叫做Mockk 。

它今天更新到版本1.7,它允许你嘲笑的对象,完全相同的方式,你的愿望。

至于它的文档:


可以按照以下方式将对象转换为嘲笑:

 object MockObj { fun add(a: Int, b: Int) = a + b } objectMockk(MockObj).use { assertEquals(3, MockObj.add(1, 2)) every { MockObj.add(1, 2) } returns 55 assertEquals(55, MockObj.add(1, 2)) } 

尽管Kotlin语言限制,如果测试逻辑需要:

 val newObjectMock = mockk() 

除非您愿意并能够更改代码,否则操作字节代码的答案是否定的。 模拟callerFunSomeObject.someFun()的调用的最直接的方式(以及我推荐的方式SomeObject.someFun()是提供一些方法来将其滑动为模拟对象。

例如

 object SomeObject { fun someFun() {} } fun callerFun() { _callerFun { SomeObject.someFun() } } internal inline fun _callerFun(caller: () -> Unit) { caller() } 

这里的想法是改变你愿意改变的东西。 如果你确定你需要一个单例和一个顶级函数作用于这个单例,那么如上所示,为了使顶层函数可测试而不改变它的公共签名,就是将它的实现移动到一个internal函数这允许滑倒模拟。