Mockito + Dexmaker on Android

2019-01-14 01:00发布

问题:

I am trying to use Mockito in my Android project. I have found very nice tutorial that deals with it: http://www.paulbutcher.com/2012/05/mockito-on-android-step-by-step/

Basically it uses new version of Mockito + Dexmaker and everything works as expected.
However, when I try to mock some Android specific object i.e:

Context context = mock(Context.class);

I receive this exception:

java.lang.IllegalArgumentException: 
    dexcache == null (and no default could be found; 
    consider setting the 'dexmaker.dexcache' system property)
at com.google.dexmaker.DexMaker.generateAndLoad(DexMaker.java:359)
at com.google.dexmaker.stock.ProxyBuilder.buildProxyClass(ProxyBuilder.java:252)
at com.google.dexmaker.mockito.DexmakerMockMaker.createMock(DexmakerMockMaker.java:54)
at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:26)

Any idea how to fix it?

回答1:

From @rjath's comment of @MrChaz's answer, this works better for me:

System.setProperty(
    "dexmaker.dexcache",
    getInstrumentation().getTargetContext().getCacheDir().getPath());

I put it in my setUp() method.



回答2:

I've managed to piece together a fix that seems to be working for me. To the manifest I added read and write external storage. To the test I added System.setProperty("dexmaker.dexcache", "/sdcard"); to the test. To the emulator image I added an SD card.

I believe this works because by default mockito tries to use the apps cache directory but I never run an activity so I suspect the directory is never created by the OS



回答3:

So the problem is with Dexmaker not being able to find the cache path on Android >= 4.3 as other people mentioned and as described in this dexmaker issue.

I went with implementing the workaround in a custom instrumented test runner instead of in every test (or their superclass) setUp(), because it feels a bit less hacky (it really is in only one place - and not inherited in every subclass) and more flexible. For the sake of documentation these are the necessary changes to do this:

public class CustomInstrumentationTestRunner extends InstrumentationTestRunner {

    @Override public void onCreate (final Bundle arguments) {
        super.onCreate(arguments);

        // temporary workaround for an incompatibility in current dexmaker (1.1) implementation and Android >= 4.3
        // cf. https://code.google.com/p/dexmaker/issues/detail?id=2 for details
        System.setProperty("dexmaker.dexcache", getTargetContext().getCacheDir().toString());
    }
}

And set up your project (or test project) to use this class as the instrumented test runner in its AndroidManifest.xml when building with ant:

<instrumentation
    android:name="my.package.CustomInstrumentationTestRunner"
    android:targetPackage="my.target.package" />

or its build.gradle when building with gradle:

android {
    defaultConfig {
        // ...
        testInstrumentationRunner 'my.package.CustomInstrumentationTestRunner'
    }
    // ...
}

If you have other instrumentation entries, you can switch between them either on the command line or select one in your IDE running configuration.



回答4:

I had this issue for an Android Library project but NOT for the application project! Setting the System property "dexmaker.dexcache" as mentioned above worked around the issue. I'm running Android 4.3 Nexus 4 device, building with 19.0.3 tools, target api 19, my dependencies:

androidTestCompile "org.mockito:mockito-core:1.9.5"
androidTestCompile "com.google.dexmaker:dexmaker:1.0"
androidTestCompile "com.google.dexmaker:dexmaker-mockito:1.0"


回答5:

It looks like the dexmaker project has moved from Google Code to GitHub.

In the maven central repository there are versions 1.1 and 1.2 published in March 2014 and December 2014.

I've verified this "dexcache == null" issue still exists through version 1.2 - but only on certain devices. For example, a Galaxy S5 with Android 5.0 has the problem, and a Galaxy S4 with Android 4.4.2 does not.

I cloned the GitHub repository (last commit March 12th 2015 - ca74669), and ran locally, and the problem has been fixed (there are also commits in the history that back this up). So once there is a 1.3 release, hopefully this problem is gone for good!

Anyone else wanting to run a local copy of 1.3-SNAPSHOT, here's how I did that (on a Mac, but other platforms should work too, you'll need mvn, adb, and dx on PATH):

  1. git clonehttps://github.com/crittercism/dexmaker.git
  2. cd dexmaker
  3. mvn install -Dmaven.test.skip=true
  4. cp -R ~/.m2/repository/com/google/dexmaker $ANDROID_HOME/extras/android/m2repository/com/google
  5. Then change version in app/build.gradle: androidTestCompile 'com.google.dexmaker:dexmaker:1.3-SNAPSHOT'
    • Or pom.xml if using maven to build, or overwrite your libs/dexmaker.jar with ~/.m2/repository/com/google/dexmaker/dexmaker/1.3-SNAPSHOT/dexmaker-1.3-SNAPSHOT.jar if you are using eclipse/ant

Also, FYI, the original issue report for the same issue on Google Code as well.



回答6:

You can add the mockito core as a dependency instead. Then, that error will not happen and you won't need a workaround.

dependencies {
   ... 
   testCompile 'org.mockito:mockito-core:1.10.19'
}