如何使用 MockK 使用私有方法模拟 Android Worker

问题描述

我是 MockK (https://mockk.io) 的新手,我正在尝试使用 MockK 在 Instrumented Test [mockk-android Version: 1.11.0](androidTest 文件夹)中测试 Android Worker。

我正在 Kotlin 中测试 Worker 实现,如下所述:https://developer.android.com/topic/libraries/architecture/workmanager/how-to/testing-worker-impl#kotlin

TL;DR:

我试图做的是监视 MyWorker。它应该像往常一样,只有私有的 Worker 函数 MyWorker.sendXmlFile(file: File?) 应该被模拟为返回 true,因为比 Worker(见代码)返回 Result.success()。正确的做法是什么?


这是要测试的 Worker:

class `MyWorker(context: Context,workerParams: WorkerParameters) : Worker(context,workerParams) {
    companion object {
        /** If the file upload fails the worker retries (Result.retry()) to upload until the upload was successful (Result.success()) or [MAX_RETRIES] is reached (Result.failure())*/
        private var MAX_RETRIES : Int = 10
        private var sharedPref : SharedPreferences? = null
        private var ftpEndpoint : String? = null
        private var ftpClient : FTPClient? = null
        private var ftpUserName : String? = null
        private var ftpPassword : String? = null
        private var ftpStatusDirectory : String? = "";
    }

    override fun doWork(): Result {
        val xmlOutput = MyObserver.getXmlOutput()
        if(xmlOutput.isNotEmpty()) {
            if(sendXmlFile(createXmlFile(xmlOutput)) {
                //[...]
                return Result.success()
            } else {
                //FileUpload was not successful
                if (runAttemptCount > MAX_RETRIES) {
                    Timber.d("{doWork()} retried %d times,max retries reached: failure",runAttemptCount)
                    return Result.failure()
                }
                Timber.d("{doWork()} retried %d times: try again",runAttemptCount)
                return Result.retry()
            }
        }
        return Result.failure()
    }

    private fun createXmlFile(xmlString : String): File {
        [...]

    }

    /**
     * Uploads a local file to the FTP server and deletes the local file afterwards
     */
    private fun sendXmlFile(file: File?) : Boolean {
        var result : Boolean = false;
        if (file?.exists() == true) {
            if(getFtpConnection@R_996_4045@ion() && connectFTP()) {
                result = FtpConnectionManager.uploadFiletoFTP(ftpClient,file)
            }
            //remove file after upload
            file.deleteOnExit()
        }
        return result
    }

}

这是我的 androidTest 文件

@RunWith(AndroidJUnit4ClassRunner::class)
class MyWorkerTest {
    private lateinit var context: Context
    private lateinit var executor: Executor

    @Before
    fun setUp() {
        context = ApplicationProvider.getApplicationContext()
        executor = Executors.newSingleThreadExecutor()
    }

@Test
    fun test() {
        var worker = TestWorkerBuilder<MyWorker>(
                context = context,executor = executor
        ).build()

        assertthat("xmlOutput is empty in the beginning",MyObserver.getXmlOutput().isNotEmpty(),`is`(false))
        var result = worker.doWork()
        assertthat("Because xmlOutput is empty worker should fail",result,`is`(ListenableWorker.Result.failure()))
       mockkObject(MyObserver.Companion)
        val xmlString = "<data>test</data>"
        every { MyObserver.Companion.getXmlOutput() } returns xmlString
        assertthat("MyObserver.getXmlOutput should be $xmlString Now",MyObserver.Companion.getXmlOutput().equals(xmlString))
        assertthat("MyObserver.getXmlOutput is not empty Now ",MyObserver.getXmlOutput().isNotEmpty())
//this is what is not working:
        val myWorker = spyk(TestWorkerBuilder<MyWorker>(
                context = context,executor = executor
        ).build(),recordPrivateCalls = true)
        every { myWorker["createXmlFile"](any()) } returns true
        result = myWorker.doWork()
        assertthat("worker will be successful Now",`is`(ListenableWorker.Result.success()))
        //unmock everything
        unmockkAll()
    }
}

这是我得到的错误

java.lang.Arrayindexoutofboundsexception: length=0; index=0
    at kotlin.reflect.jvm.internal.impl.descriptors.runtime.structure.ReflectJavaMember.getValueParameters(ReflectJavaMember.kt:59)
    at kotlin.reflect.jvm.internal.impl.descriptors.runtime.structure.ReflectJavaMethod.getValueParameters(ReflectJavaMethod.kt:26)
    at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaScope.resolveMethodToFunctionDescriptor(LazyJavaScope.kt:153)
    at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaScope$declaredFunctions$1.invoke(LazyJavaScope.kt:94)
    at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaScope$declaredFunctions$1.invoke(LazyJavaScope.kt:59)
    at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$MapBasedMemoizedFunction.invoke(LockBasedStorageManager.java:512)
    at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$MapBasedMemoizedFunctionToNotNull.invoke(LockBasedStorageManager.java:587)
    at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaScope$declaredFunctions$1.invoke(LazyJavaScope.kt:89)
    at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaScope$declaredFunctions$1.invoke(LazyJavaScope.kt:59)
    at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$MapBasedMemoizedFunction.invoke(LockBasedStorageManager.java:512)
    at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$MapBasedMemoizedFunctionToNotNull.invoke(LockBasedStorageManager.java:587)
    at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaScope$functions$1.invoke(LazyJavaScope.kt:117)
    at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaScope$functions$1.invoke(LazyJavaScope.kt:59)
    at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$MapBasedMemoizedFunction.invoke(LockBasedStorageManager.java:512)
    at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$MapBasedMemoizedFunctionToNotNull.invoke(LockBasedStorageManager.java:587)
    at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaScope.getContributedFunctions(LazyJavaScope.kt:267)
    at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaClassMemberScope.getContributedFunctions(LazyJavaClassMemberScope.kt:747)
    at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaClassMemberScope.getFunctionsFromSupertypes(LazyJavaClassMemberScope.kt:438)
    at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaClassMemberScope.computeNonDeclaredFunctions(LazyJavaClassMemberScope.kt:268)
    at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaScope$functions$1.invoke(LazyJavaScope.kt:121)
    at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaScope$functions$1.invoke(LazyJavaScope.kt:59)
    at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$MapBasedMemoizedFunction.invoke(LockBasedStorageManager.java:512)
    at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$MapBasedMemoizedFunctionToNotNull.invoke(LockBasedStorageManager.java:587)
    at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaScope.getContributedFunctions(LazyJavaScope.kt:267)
    at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaClassMemberScope.getContributedFunctions(LazyJavaClassMemberScope.kt:747)
    at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedClassDescriptor$DeserializedClassMemberScope.computeNonDeclaredFunctions(DeserializedClassDescriptor.kt:242)
    at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedMemberScope.computeFunctions(DeserializedMemberScope.kt:102)
    at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedMemberScope.access$computeFunctions(DeserializedMemberScope.kt:40)
    at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedMemberScope$functions$1.invoke(DeserializedMemberScope.kt:66)
    at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedMemberScope$functions$1.invoke(DeserializedMemberScope.kt:40)
    at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$MapBasedMemoizedFunction.invoke(LockBasedStorageManager.java:512)
    at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$MapBasedMemoizedFunctionToNotNull.invoke(LockBasedStorageManager.java:587)
    at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedMemberScope.getContributedFunctions(DeserializedMemberScope.kt:139)
    at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedClassDescriptor$DeserializedClassMemberScope.getContributedFunctions(DeserializedClassDescriptor.kt:231)
    at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedMemberScope.addFunctionsAndProperties(DeserializedMemberScope.kt:221)
    at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedMemberScope.computeDescriptors(DeserializedMemberScope.kt:181)
    at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedClassDescriptor$DeserializedClassMemberScope$allDescriptors$1.invoke(DeserializedClassDescriptor.kt:217)
    at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedClassDescriptor$DeserializedClassMemberScope$allDescriptors$1.invoke(DeserializedClassDescriptor.kt:210)
    at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$LockBasedLazyValue.invoke(LockBasedStorageManager.java:355)
    at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$LockBasednotNullLazyValue.invoke(LockBasedStorageManager.java:474)
    at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedClassDescriptor$DeserializedClassMemberScope.getContributedDescriptors(DeserializedClassDescriptor.kt:227)
    at kotlin.reflect.jvm.internal.impl.resolve.scopes.ResolutionScope$DefaultImpls.getContributedDescriptors$default(ResolutionScope.kt:52)
    at kotlin.reflect.jvm.internal.KDeclarationContainerImpl.getMembers(KDeclarationContainerImpl.kt:65)
    at kotlin.reflect.jvm.internal.KClassImpl$Data$declarednonStaticmembers$2.invoke(KClassImpl.kt:159)
    at kotlin.reflect.jvm.internal.KClassImpl$Data$declarednonStaticmembers$2.invoke(KClassImpl.kt:44)
    at kotlin.reflect.jvm.internal.ReflectProperties$LazySoftVal.invoke(ReflectProperties.java:92)
    at kotlin.reflect.jvm.internal.ReflectProperties$Val.getValue(ReflectProperties.java:31)
    at kotlin.reflect.jvm.internal.KClassImpl$Data.getDeclarednonStaticmembers(UnkNown Source:8)
    at kotlin.reflect.jvm.internal.KClassImpl$Data$allNonStaticmembers$2.invoke(KClassImpl.kt:168)
    at kotlin.reflect.jvm.internal.KClassImpl$Data$allNonStaticmembers$2.invoke(KClassImpl.kt:44)
    at kotlin.reflect.jvm.internal.ReflectProperties$LazySoftVal.invoke(ReflectProperties.java:92)
    at kotlin.reflect.jvm.internal.ReflectProperties$Val.getValue(ReflectProperties.java:31)
    at kotlin.reflect.jvm.internal.KClassImpl$Data.getAllNonStaticmembers(UnkNown Source:8)
    at kotlin.reflect.jvm.internal.KClassImpl$Data$allMembers$2.invoke(KClassImpl.kt:174)
    at kotlin.reflect.jvm.internal.KClassImpl$Data$allMembers$2.invoke(KClassImpl.kt:44)
    at kotlin.reflect.jvm.internal.ReflectProperties$LazySoftVal.invoke(ReflectProperties.java:92)
    at kotlin.reflect.jvm.internal.ReflectProperties$Val.getValue(ReflectProperties.java:31)
    at kotlin.reflect.jvm.internal.KClassImpl$Data.getAllMembers(UnkNown Source:8)
    at kotlin.reflect.jvm.internal.KClassImpl.getMembers(KClassImpl.kt:192)
    at kotlin.reflect.full.KClasses.getFunctions(KClasses.kt:90)
    at io.mockk.InternalPlatformdsl.dynamicCall(InternalPlatformdsl.kt:99)
    at io.mockk.MockKMatcherScope$DynamicCall.invoke(API.kt:1980)
    at com.umlaut.thc.tnaappdesign.MyWorkerTest$test$2.invoke(MyWorkerTest.kt:98)
    at com.example.MyWorkerTest$test$2.invoke(MyWorkerTest.kt:29)
    at io.mockk.impl.eval.RecordedBlockEvaluator$record$block$1.invoke(RecordedBlockEvaluator.kt:25)
    at io.mockk.impl.eval.RecordedBlockEvaluator$enhanceWithRethrow$1.invoke(RecordedBlockEvaluator.kt:78)
    at io.mockk.impl.recording.JvmAutoHinter.autoHint(JvmAutoHinter.kt:23)
    at io.mockk.impl.eval.RecordedBlockEvaluator.record(RecordedBlockEvaluator.kt:40)
    at io.mockk.impl.eval.EveryBlockEvaluator.every(EveryBlockEvaluator.kt:30)
    at io.mockk.MockKDsl.internaLevery(API.kt:92)
    at io.mockk.MockKKt.every(MockK.kt:98)
    at com.example.MyWorkerTest.test(MyWorkerTest.kt:89)
    at java.lang.reflect.Method.invoke(Native Method)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at androidx.test.internal.runner.junit4.statement.RunBefores.evaluate(RunBefores.java:80)
    at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
    at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
    at org.junit.runners.BlockJUnit4ClassRunner.runchild(BlockJUnit4ClassRunner.java:103)
    at org.junit.runners.BlockJUnit4ClassRunner.runchild(BlockJUnit4ClassRunner.java:63)
    at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
    at org.junit.runners.ParentRunner.runchildren(ParentRunner.java:329)
    at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
    at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
    at org.junit.runners.Suite.runchild(Suite.java:128)
    at org.junit.runners.Suite.runchild(Suite.java:27)
    at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
    at org.junit.runners.ParentRunner.runchildren(ParentRunner.java:329)
    at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
    at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
    at androidx.test.internal.runner.TestExecutor.execute(TestExecutor.java:56)
    at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:395)
    at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2238)

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)