如何在Mockito和Kotlin中模拟File.copyTo

ru9i0ody  于 12个月前  发布在  Kotlin
关注(0)|答案(3)|浏览(168)

简单地说,我有一个File对象,产品代码将在该对象上调用copyTo方法。
在单元测试中,我只想要一个模拟文件,copyTo调用是一个no-op或者最好是经过验证的。
简单的例子:

fun copyFileTest() {
        println("start test")
        val mockFileSrc = mock(File::class.java)
        val mockFileDst = mock(File::class.java)

        `when`(mockFileSrc.exists()).doReturn(true)
        `when`(mockFileSrc.copyTo(any(), any(), any())).thenAnswer { // DOES NOT WORK
            val result = it.arguments[0]
            result as File
        }

        println("done initializing mocks")

        Assert.assertEquals(mockFileSrc.exists(), true)
        mockFileSrc.copyTo(mockFileDst, true, 0)

        println("done with test")
    }

字符串
当单元测试运行时,将引发此异常:

Parameter specified as non-null is null: method kotlin.io.FilesKt__UtilsKt.copyTo, parameter target
java.lang.NullPointerException: Parameter specified as non-null is null: method kotlin.io.FilesKt__UtilsKt.copyTo, parameter target
    at kotlin.io.FilesKt__UtilsKt.copyTo(Utils.kt)
    at com.selibe.myapp.foo.WorkerTest.copyFileTest(WorkerTest.kt:121) <34 internal lines>
    at jdk.proxy2/jdk.proxy2.$Proxy5.processTestClass(Unknown Source) <7 internal lines>
    at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
    at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74


我相信这个问题可能与copyTo似乎是一个扩展函数有关。
什么是最简单的方法,只是使copyTo在单元测试中的一个空操作,总是成功的单元测试?
mockito或mockk解决方案是可接受的。

yebdmbv4

yebdmbv41#

推荐使用**mockk**库,支持Kotlin扩展函数,Java不支持扩展函数,您正在尝试在代码中使用extension function

样品 Package 箱

import io.mockk.mockkStatic

@Test
    fun `copy to cache should copy file to right folder`() {
        // Given
        val context = mockk<Context>(relaxed = true)
        every { context.cacheDir.path } returns "folder/"

        mockkStatic("kotlin.io.FilesKt__UtilsKt")
        every { any<File>().copyTo(any(), any(), any()) } returns mockk(relaxed = true)

        val source = File("image.jpg")

        // When
        copyToCache(context, File("image.jpg"))

        // Then
        verify {
            source.copyTo(File("folder/compressor/image.jpg"), true, any())
        }
    }

字符串
您可以根据需要使用其他扩展功能。

您的案例

import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import java.io.File

    class CopyTestFile {
        @Test
        fun `copy to file`() {
            
            val mockFileSrc = mockk<File>()
            val mockFileDst = mockk<File>()
    
            mockkStatic("kotlin.io.FilesKt__UtilsKt")
            every { mockFileSrc.exists() } returns true
            every { mockFileSrc.copyTo(any(), any(), any()) } answers {
                arg(0) as File
            }
    
            assertTrue(mockFileSrc.exists())
            mockFileSrc.copyTo(mockFileDst, true, 0)
        }
    }


支持@K314159的回答

fnx2tebb

fnx2tebb2#

你遇到的问题是File.copyTo是一个Kotlin extension function。Mockito是一个Java框架,并且不理解任何关于Kotlin扩展函数的东西。
Java不支持扩展函数,所以File.copyTo实际上在JVM字节码中编译成的是一个静态函数,是类kotlin.io.UtilsKt的成员,它在参数列表的开头接受一个额外的参数,表示接收者对象(源文件),如下所示:

public static final File copyTo(
        File $this$copyTo,
        File target,
        boolean overwrite,
        int bufferSize)

字符串
如果你想使用Mockito来模拟它,你需要在kotlin.io.UtilsKt类上使用mockStatic,然后模拟静态方法copyTo。如果可能的话,切换到为Kotlin编写的mocking框架(如Mockk)会容易得多。
使用Mockk时,要模拟模块范围的扩展函数,请按照Mockk网站上的说明使用mockkStatic。下面是原始代码的等效代码,已翻译为使用Mockk:

import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Test
import java.io.File

class MyTest {
    @Test
    fun copyFileTest() {
        println("start test")
        val mockFileSrc = mockk<File>()
        val mockFileDst = mockk<File>()

        mockkStatic(File::copyTo)
        every { mockFileSrc.exists() } returns true
        every { mockFileSrc.copyTo(any(), any(), any()) } answers {
            arg(1) as File  // arg(0) is `this` (source), arg(1) is dest
        }

        println("done initializing mocks")

        assertTrue(mockFileSrc.exists())
        mockFileSrc.copyTo(mockFileDst, true, 0)

        println("done with test")
    }
}

snz8szmq

snz8szmq3#

我肯定不会在这里赢得积分,但我忍不住想知道......为什么你要试图模拟完全可以在单元测试中工作的类?
因为jUnit可以访问Java API和KotlinAPI。
所有你的缓存代码需要JVM投诉是删除硬依赖的android jar.换句话说:

val context = mockk<Context>(relaxed = true)
        every { context.cacheDir.path } returns "folder/"

字符串
cacheDir.path是一个String。你的方法应该接收一个String作为参数,而不是直接读取一个android上下文。然后你可以直接运行你的代码,不需要mock,并尝试所有你可能遇到的角落情况。或者对你的代码进行压力测试。
我在我的最新职位上做了这件事。我必须确保4个独立的进程,都能够访问相同的文件和目录,不会在它们之间崩溃。所以我做了一个JVM实现,并将其子类化以添加仅限Android的东西,比如访问上下文。测试同时运行11000个操作。所以我知道它有效。是的,移动和复制文件是测试操作的一部分。而copyTo是我使用的API之一。
记住,你并不依赖于android。你是一个JVM开发者。Android只是你的主要环境。

相关问题