当前位置: 首页 > 知识库问答 >
问题:

如何在Kotlin模拟对象?

须鸿祯
2023-03-14

我想测试一个调用对象的类(java中的静态方法调用),但我不能模拟这个对象以避免执行真正的方法。

object Foo {
    fun bar() {
        //Calls third party sdk here
    }
}
@RunWith(PowerMockRunner::class)
@PrepareForTest(IntentGenerator::class)
class EditProfilePresenterTest {

    @Test
    fun shouldCallIntentGenerator() {

        val intent = mock(Intent::class.java)

        PowerMockito.mockStatic(IntentGenerator::class.java)
        PowerMockito.`when`(IntentGenerator.newIntent(any())).thenReturn(intent) //newIntent method param is context

       presenter.onGoToProfile()

       verify(view).startActivity(eq(intent))        

    }
}

any()方法来自mockito_kotlin。然后,如果我将一个模拟上下文传递给newIntent方法,则似乎调用了real方法。

共有1个答案

荣声
2023-03-14

首先,对象IntentGenerator看起来像是一种代码气味,为什么要使它成为对象?如果它不是您的代码,您可以轻松地创建一个包装类

class IntentGeneratorWrapper {

    fun newIntent(context: Context) = IntentGenerator.newIntent(context)    

}

并在代码中使用它,而不需要静态依赖关系。

话虽如此,我有两个解决方案。假设您有一个对象

object IntentGenerator {
    fun newIntent(context: Context) = Intent()
}
testCompile "io.mockk:mockk:1.7.10"
testCompile "com.nhaarman:mockito-kotlin:1.5.0"

然后在测试中使用objectmockkfun与object作为参数,这将返回一个作用域,在该作用域上调用use。在use正文中,您可以模拟对象:

@Test
fun testWithMockk() {
    val intent: Intent = mock()
    whenever(intent.action).thenReturn("meow")

    objectMockk(IntentGenerator).use {
        every { IntentGenerator.newIntent(any()) } returns intent
        Assert.assertEquals("meow", IntentGenerator.newIntent(mock()).action)
    }
}

解决方案2-模仿+反射

在您的测试资源文件夹中创建一个mockito-extensions文件夹(例如,如果您的模块是“app”->app/src/test/resources/mockito-extensions),并在其中创建一个名为org.mockito.plugins.mockmaker的文件。在文件中,只需编写这一行mock-maker-inline。现在可以模拟final类和方法(IntentGenerator类和NewIntent方法都是final的)。

那你需要

  1. 创建IntentGenerator的实例。请注意,IntentGenerator只是一个普通的java类,我邀请您在Android Studio
  2. 中通过 Kotlin Bytecode窗口检查它
  3. 在该实例上使用Mockito创建spy对象并模拟该方法
  4. instance字段中删除最终修饰符。在Kotlin中声明Object时,发生的情况是使用私有构造函数和静态Instance方法创建类(在本例中为IntentGenerator)。即单例。
  5. IntentGenerator.Instance值替换为您自己的模拟实例。

完整的方法如下所示:

@Test
fun testWithReflection() {
    val intent: Intent = mock()
    whenever(intent.action).thenReturn("meow")

    // instantiate IntentGenerator
    val constructor = IntentGenerator::class.java.declaredConstructors[0]
    constructor.isAccessible = true
    val intentGeneratorInstance = constructor.newInstance() as IntentGenerator

    // mock the the method
    val mockedInstance = spy(intentGeneratorInstance)
    doAnswer { intent }.`when`(mockedInstance).newIntent(any())

    // remove the final modifier from INSTANCE field
    val instanceField = IntentGenerator::class.java.getDeclaredField("INSTANCE")
    val modifiersField = Field::class.java.getDeclaredField("modifiers")
    modifiersField.isAccessible = true
    modifiersField.setInt(instanceField, instanceField.modifiers and Modifier.FINAL.inv())

    // set your own mocked IntentGenerator instance to the static INSTANCE field
    instanceField.isAccessible = true
    instanceField.set(null, mockedInstance)

    // and BAM, now IntentGenerator.newIntent() is mocked
    Assert.assertEquals("meow", IntentGenerator.newIntent(mock()).action)
}

问题是,在模拟对象之后,模拟的实例将停留在那里,其他测试可能会受到影响。A制作了一个示例,说明如何将嘲讽限制在一个范围内

为什么PowerMock不能工作

PowerMockito.mockStatic(IntentGenerator::class.java)
PowerMockito.doAnswer { intent }.`when`(IntentGenerator).newIntent(any())

notaMockException:传递给when()不是模拟的参数!

如果对象被模拟,则调用的newInstance方法将不是来自实际类的方法,因此即使在签名中null不可为空,也可以作为参数传递

 类似资料:
  • 给定一个Kotlin单例对象和一个调用它的方法的乐趣

  • 我有一个示例方法(我需要编写测试用例)如下所示, 我想模拟getConfig方法并返回一个特定的字符串值。getConfig是Kotlin对象中方法,如下所示, 下面是我尝试的测试 我没有得到任何错误,但是getConfig方法没有被嘲笑。执行实际的实现。我也试过使用Powermockito。请帮帮我

  • 我试图用Kotlin中的jMockit模拟一个静态方法: 但我得到以下错误:

  • 我想模拟注释来检查根据给定注释返回结果的类的良好行为。 Erg.mockito.exceptions.misusing.WurnTypeOfReturnValue:annotationType()不能返回KClassImpl annotationType()应该返回Class 如果你不确定为什么你会超过错误,请继续阅读。由于语法的性质,上面的问题可能会发生,因为: 此异常可能发生在错误编写的多线程

  • 如何模拟返回已强制转换的模拟对象的方法。 试验方法。

  • 问题内容: 我在Python2中有以下代码段: 我想为其编写一些单元测试。我想知道如何使用它来修补游标和连接变量,以便它们返回伪造的数据集?我已经为单元测试尝试了以下代码段,但无济于事: 但是我似乎收到以下错误: 问题答案: 由于游标是的返回值,因此您只需模拟连接,然后正确配置它。例如,