2016-06-22 56 views
6

给定科特林单对象,并调用它的方法如何模拟Kotlin单例对象?

object SomeObject { 
    fun someFun() {} 
} 

fun callerFun() { 
    SomeObject.someFun() 
} 

有没有一种方法来模拟来电SomeObject.someFun()一个有趣?

回答

5

只是让你的对象实现一个接口,比你可以嘲笑你与任何嘲讽库反对。 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) 
    } 
} 

或者在情况下,如果你想模仿SomeObjectcallerFun

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) 
    } 
} 
+0

这看起来像一个反模式...一个不应该在主代码中创建额外的类/接口只是为了测试的目的! – Kerooker

+0

没有额外的类,只是接口。你应该这样做,因为这是在JVM上测试的最佳实践。 – IRus

0

操作字节码的缺点是答案是否定的,除非您愿意并且能够更改代码。以最直接的方式(以及我推荐的方式)模拟callerFunSomeObject.someFun()的呼叫是提供某种方式将其滑入模拟对象。

例如

object SomeObject { 
    fun someFun() {} 
} 

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

internal inline fun _callerFun(caller:() -> Unit) { 
    caller() 
} 

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

2

有用于科特林一个非常好的新的嘲弄库。它叫做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)) 
} 

尽管科特林语言的限制,您可以创建,如果对象的新实例测试逻辑需要:

val newObjectMock = mockk<MockObj>() 
2

您可以使用类代表来模拟没有任何额外库的对象。

这里是我的建议

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初始化后,委托将处理所有的功能。
更多你可以找到官方documentation