使用 Mockk 模拟静态 Java 方法



我们目前正在使用 java 和 kotlin 项目,慢慢地将整个代码迁移到后者。

是否可以使用 Mockk 模拟像Uri.parse()这样的静态方法?

示例代码的外观如何?

在 mockk 1.8.1 之后:

Mockk 版本 1.8.1 弃用了下面的解决方案。在该版本之后,您应该执行以下操作:

@Before
fun mockAllUriInteractions() {
    mockkStatic(Uri::class)
    val uriMock = mockk<Uri>() 
    every { Uri.parse("test/path") } returns uriMock
}

每次调用mockkStatic时都会被清除,因此在再次使用它之前无需取消模拟它。

但是,如果该静态命名空间将在测试之间共享,它将共享模拟行为。为避免这种情况,请确保在套件完成后unmockkStatic

<小时 />

荒废的:

如果您需要模拟行为始终存在,而不仅仅是在单个测试用例中,您可以使用@Before@After来模拟它:

@Before
fun mockAllUriInteractions() {
    staticMockk<Uri>().mock()
    every { Uri.parse("http://test/path") } returns Uri("http", "test", "path")    //This line can also be in any @Test case
}
@After
fun unmockAllUriInteractions() {
    staticMockk<Uri>().unmock()
}

这样,如果您希望类的更多部分使用 Uri 类,则可以在一个位置模拟它,而不是到处用.use污染代码。

当心

如果在没有块的情况下调用mockkSatic(),请不要忘记在调用模拟方法后调用unmockkStatic()。该方法不会自动取消模拟,即使在不调用 mockkStatic() 但使用静态方法的不同测试类中,您仍然会获得模拟值。

另一种选择是在块内执行模拟方法,然后它将自动取消模拟:

mockkStatic(Uri::class) {
    every { Uri.parse("http://test/path") } returns Uri("http", "test", "path")
    val uri = Uri.parse("http://test/path")
}

MockK 允许模拟静态 Java 方法。它的主要目的是模拟 Kotlin 扩展函数,因此它不如 PowerMock 强大,但即使对于 Java 静态方法,它仍然可以正常工作。

语法如下:

staticMockk<Uri>().use {
    every { Uri.parse("http://test/path") } returns Uri("http", "test", "path")
    assertEquals(Uri("http", "test", "path"), Uri.parse("http://test/path"))
    verify { Uri.parse("http://test/path") }  
}

更多详情请见:http://mockk.io/#extension-functions

除了接受的答案:

你不能创建这样的Uri,你也必须模拟 Uri 实例。像这样:

private val mockUri = mockk<Uri>()
@Before
fun mockAllUriInteractions() {
    mockkStatic(Uri::class)
    every { Uri.parse("http://test/path") } returns mockUri
    // or just every { Uri.parse("http://test/path") } returns mockk<Uri>()
}
如果我们

要模拟静态,比如:mockkStatic(Klass::class(

那么我们肯定必须取消模拟它,比如:unmockkStatic(Klass::class(

我建议在注释的方法中取消模拟它@After。

一个完整的示例是:

class SomeTest {
  private late var viewMode: SomeViewModel
  @Before
  fun setUp() {
    viewMode = SomeViewModel()
    mockkStatic(OurClassWithStaticMethods::class)       
  }
  @After
  fun tearDown() {
    unmockkStatic(OurClassWithStaticMethods::class)
  }
  @Test
  fun `Check that static method get() in the class OurClassWithStaticMethods was called`() {
    //Given
    every { OurClassWithStaticMethods.get<Any>(any()) } returns "dummyString"
    //When
    viewModel.doSomethingWhereStaticMethodIsCalled()
    //Then
    verify(exactly = 1) { 
       OurClassWithStaticMethods.get<Any>(any()) 
    }
  }
}

这个例子是用模拟库"Mockk"v.1.12.0编写

如上面的多个答案所述,您必须确保调用unmockkStatic - 否则您最终会得到片状测试(因为模拟的对象/函数将在测试类中可用。

在我的场景中 - 我在 kotlin 中有一个模块范围的扩展函数,并嘲笑我使用了如下所示的配套对象:

class SampleTest {
companion object {
    @BeforeAll
    @JvmStatic
    fun setup() {
        mockkStatic("packagename.filenameKt")
    }
    @AfterAll
    @JvmStatic
    fun teardown() {
        unmockkStatic("packagename.filenameKt")
    }
}}
// Add @RunWith(RobolectricTestRunner::class) on top of 
// your class since it provides access to Android framework APIs.
// Test case written inside the `mockkStatic` method to 
// verify the behavior of a method that involves a static method call in Kotlin
        
            @Test
            fun `my test case`() = runBlocking {
                // Mocking the Log class
                mockkStatic(Log::class) {
                    // Test case to verify the behavior of a method 
                    // that involves a log method call
                }
            }


// OR just use `mockkObject(Log)` without any block 

最新更新