简体   繁体   English

Robolectric在命令行上失败但在Android Studio中成功

[英]Robolectric fails on command line but succeeds in Android Studio

I have a test where I use Robolectric which succeeds in Android Studio, but does not on the command line. 我有一个测试,我使用Robolectric在Android Studio中取得成功,但不在命令行上。

I use Robolectric 4.2 and the test involves a cipher which I partially mock for this test. 我使用的是Robolectric 4.2,测试涉及一个密码,我对此测试进行了部分模拟。

//How the cipher is created
val mockCipher = object : Cipher(MockCipherSpi(), null, null) {}

The MockCipher basically just returns the unencrypted input: MockCipher基本上只返回未加密的输入:

class MockCipherSpi : CipherSpi() {
...
    private val algorithmParametersSpi: AlgorithmParametersSpi? = object : AlgorithmParametersSpi() {
        ...
        override fun <T : AlgorithmParameterSpec?> engineGetParameterSpec(paramSpec: Class<T>?): T {
            return IvParameterSpec(byteArrayOf()) as T
        }
    }
    ...
    override fun engineGetParameters(): AlgorithmParameters {
        return object : AlgorithmParameters(algorithmParametersSpi, null, null) {
            init {
                init(byteArrayOf())
            }
        }
    }
}

The reason my test fails is that I get a null pointer exception when I try to get the IV from the cipher: 我的测试失败的原因是当我尝试从密码中获取IV时,我得到一个空指针异常:

cipher.parameters.getParameterSpec(IvParameterSpec::class.java).iv

This works perfectly fine for Android Studio and I can even debug it, but running it from the command line with ./gradlew test this fails. 这适用于Android Studio,我甚至可以调试它,但是使用./gradlew test从命令行运行它会失败。

java.lang.NullPointerException
at javax.crypto.Cipher.<init>(Cipher.java:268)
at the place where I try to access the iv
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.robolectric.RobolectricTestRunner$HelperTestRunner$1.evaluate(RobolectricTestRunner.java:601)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.robolectric.internal.SandboxTestRunner$2.evaluate(SandboxTestRunner.java:260)
at org.robolectric.internal.SandboxTestRunner.runChild(SandboxTestRunner.java:130)
at org.robolectric.internal.SandboxTestRunner.runChild(SandboxTestRunner.java:42)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.robolectric.internal.SandboxTestRunner$1.evaluate(SandboxTestRunner.java:84)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:116)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:59)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:39)
at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:66)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
at com.sun.proxy.$Proxy1.processTestClass(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:109)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:146)
at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:128)
at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:404)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
at java.lang.Thread.run(Thread.java:748)

It seems there are two javax.crypto.Cipher one from the in the android.jar and one in the jce.jar I assume that AS is using one and the gradle command the other. 似乎有两个来自android.jar的javax.crypto.Cipher和jce.jar中的一个我假设AS正在使用一个而gradle命令另一个。

Do I have to tell gradle to use an other jar or can I solve this problem otherwise? 我是否必须告诉gradle使用其他罐子,否则我可以解决这个问题吗?

You are trying to use a class from the android SDK in a desktop test. 您正尝试在桌面测试中使用android SDK中的类。 That's always going to fail. 那总是会失败的。 The Android SDK has classes that simply aren't available in the Java Development Kit. Android SDK具有Java Development Kit中不可用的类。 The Android jar provides just an empty API for development purposes, but to run the programs, you need to execute them into an android device. Android jar提供了一个空的API用于开发目的,但是要运行程序,您需要将它们执行到Android设备中。 In this case, you would need to either mock the entire call, or locate an equivalent desktop implementation and replace the android calls with the desktop ones during the test (maybe by splitting your class in two, logic and API access for example). 在这种情况下,您需要模拟整个调用,或者找到一个等效的桌面实现,并在测试期间将android调用替换为桌面调用(例如,可以将您的类拆分为两个,例如逻辑和API访问)。 Given that the methods you are running are exposing a native (C level) api, mocking the entire API call looks like the fastest way to solve this into the test. 鉴于您运行的方法是暴露本机(C级)API,模拟整个API调用看起来是将其解决为测试的最快方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM