Code Monkey home page Code Monkey logo

dexmaker's Introduction

Dexmaker

Build Status

A Java-language API for doing compile time or runtime code generation targeting the Dalvik VM. Unlike cglib or ASM, this library creates Dalvik .dex files instead of Java .class files.

It has a small, close-to-the-metal API. This API mirrors the Dalvik bytecode specification giving you tight control over the bytecode emitted. Code is generated instruction-by-instruction; you bring your own abstract syntax tree if you need one. And since it uses Dalvik's dx tool as a backend, you get efficient register allocation and regular/wide instruction selection for free.

What does it do?

Mockito Mocks

Dexmaker lets you use the Mockito mocking library in your Android projects by generating Dalvik bytecode class proxies. Just add an androidTestImplementation dependency on dexmaker-mockito and you can use Mockito in your Android Instrumentation tests.

The version of Mockito that Dexmaker targets can be found in dexmaker-mockito's build.gradle file. The general rule is that the major and minor version of Dexmaker will match the underlying major and minor version of Mockito.

Mocking Final Classes & Methods

Starting in Android "P", it is possible to mock final classes and methods using the dexmaker-mockito-inline library. If you execute your tests on a device or emulator running Android P or above, you can add an androidTestImplementation dependency on dexmaker-mockito-inline (instead of dexmaker-mockito; don't add both) and you can use the normal Mockito APIs to mock final classes and methods in your Android Instrumentation tests.

NOTE: This functionality requires OS APIs which were introduced in Android P and cannot work on older versions of Android.

Class Proxies

Dexmaker includes a stock code generator for class proxies. If you just want to do AOP or class mocking, you don't need to mess around with bytecodes.

Runtime Code Generation

This example generates a class and a method. It then loads that class into the current process and invokes its method.

public final class HelloWorldMaker {
    public static void main(String[] args) throws Exception {
        DexMaker dexMaker = new DexMaker();

        // Generate a HelloWorld class.
        TypeId<?> helloWorld = TypeId.get("LHelloWorld;");
        dexMaker.declare(helloWorld, "HelloWorld.generated", Modifier.PUBLIC, TypeId.OBJECT);
        generateHelloMethod(dexMaker, helloWorld);

        // Create the dex file and load it.
        File outputDir = new File(".");
        ClassLoader loader = dexMaker.generateAndLoad(HelloWorldMaker.class.getClassLoader(),
                outputDir, outputDir);
        Class<?> helloWorldClass = loader.loadClass("HelloWorld");

        // Execute our newly-generated code in-process.
        helloWorldClass.getMethod("hello").invoke(null);
    }

    /**
     * Generates Dalvik bytecode equivalent to the following method.
     *    public static void hello() {
     *        int a = 0xabcd;
     *        int b = 0xaaaa;
     *        int c = a - b;
     *        String s = Integer.toHexString(c);
     *        System.out.println(s);
     *        return;
     *    }
     */
    private static void generateHelloMethod(DexMaker dexMaker, TypeId<?> declaringType) {
        // Lookup some types we'll need along the way.
        TypeId<System> systemType = TypeId.get(System.class);
        TypeId<PrintStream> printStreamType = TypeId.get(PrintStream.class);

        // Identify the 'hello()' method on declaringType.
        MethodId hello = declaringType.getMethod(TypeId.VOID, "hello");

        // Declare that method on the dexMaker. Use the returned Code instance
        // as a builder that we can append instructions to.
        Code code = dexMaker.declare(hello, Modifier.STATIC | Modifier.PUBLIC);

        // Declare all the locals we'll need up front. The API requires this.
        Local<Integer> a = code.newLocal(TypeId.INT);
        Local<Integer> b = code.newLocal(TypeId.INT);
        Local<Integer> c = code.newLocal(TypeId.INT);
        Local<String> s = code.newLocal(TypeId.STRING);
        Local<PrintStream> localSystemOut = code.newLocal(printStreamType);

        // int a = 0xabcd;
        code.loadConstant(a, 0xabcd);

        // int b = 0xaaaa;
        code.loadConstant(b, 0xaaaa);

        // int c = a - b;
        code.op(BinaryOp.SUBTRACT, c, a, b);

        // String s = Integer.toHexString(c);
        MethodId<Integer, String> toHexString
                = TypeId.get(Integer.class).getMethod(TypeId.STRING, "toHexString", TypeId.INT);
        code.invokeStatic(toHexString, s, c);

        // System.out.println(s);
        FieldId<System, PrintStream> systemOutField = systemType.getField(printStreamType, "out");
        code.sget(systemOutField, localSystemOut);
        MethodId<PrintStream, Void> printlnMethod = printStreamType.getMethod(
                TypeId.VOID, "println", TypeId.STRING);
        code.invokeVirtual(printlnMethod, null, localSystemOut, s);

        // return;
        code.returnVoid();
    }
}

Download

For Mockito support, download the latest .jar via Maven:

    <dependency>
      <groupId>com.linkedin.dexmaker</groupId>
      <artifactId>dexmaker-mockito</artifactId>
      <version>2.28.3</version>
      <type>pom</type>
    </dependency>

or Gradle:

    androidTestImplementation 'com.linkedin.dexmaker:dexmaker-mockito:2.28.3'

Note: The dependency on Mockito will be transitively included, so there's no need to specify both Mockito AND dexmaker-mockito

Snapshots

You can use snapshot builds to test the latest unreleased changes. A new snapshot is published after every merge to the main branch by the Deploy Snapshot Github Action workflow.

Just add the Sonatype snapshot repository to your Gradle scripts:

repositories {
    maven {
        url "https://oss.sonatype.org/content/repositories/snapshots/"
    }
}

You can find the latest snapshot version to use in the gradle.properties file.

dexmaker's People

Contributors

agampe avatar awesdroid avatar brettchabot avatar cckroets avatar dreifachstein avatar drewhannay avatar dshirley avatar ened avatar euporie avatar jameskbride avatar johnjohndoe avatar kkoser avatar li-wjohnson avatar liutikas avatar lyrachord avatar mahuna13 avatar markb74 avatar mockitoguy avatar moltmann avatar narayank avatar paulduffin avatar pylaligand avatar robkwok avatar sbgoog avatar swankjesse avatar tdrhq avatar timvdlippe avatar tmurakami avatar ttanxu avatar vmutafov avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dexmaker's Issues

When using ProxyBuilder#withSharedClassLoader(), the proxy classes for the same class will be the same even though different interfaces are specified by implementing()

  • dexmaker 2.19.1
public class ProxyBuilderTest {

    @Test
    public void test() throws IOException {
        Class<?> c1 = ProxyBuilder.forClass(C.class)
                                  .implementing(I1.class)
                                  .withSharedClassLoader()
                                  .buildProxyClass();
        Class<?> c2 = ProxyBuilder.forClass(C.class)
                                  .implementing(I2.class)
                                  .withSharedClassLoader()
                                  .buildProxyClass();
        assertNotSame(c1, c2); // java.lang.AssertionError: expected not same
    }

    static class C {
    }

    interface I1 {
    }

    interface I2 {
    }

}

Is it possible to spy Context?

Hi,

Whether it is possible to do the following?

Context context = spy(InstrumentationRegistry.getTargetContext());

It fails on emulator API 17 (however mock() works without a problem)

W/dalvikvm: Superclass of 'LContextImpl_Proxy;' (Landroid/app/ContextImpl;) is not accessible
W/dalvikvm: Link of class 'LContextImpl_Proxy;' failed

Here is my build.gradle

androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
})
androidTestCompile 'com.linkedin.dexmaker:dexmaker:2.2.0'
androidTestCompile 'com.linkedin.dexmaker:dexmaker-mockito:2.2.0' 

What I've already tried:

  1. Add file androidTest/resources/mockito-extension/org.mockito.plugins.MockMaker with the following contents
com.android.dx.mockito.DexmakerMockMaker
  1. Add this line to setup() method
System.setProperty("dexmaker.dexcache", getInstrumentation().getTargetContext().getCacheDir().getPath());

And still no luck...
Any advice, guys?

Cannot mock concrete classes with Mockito

I'm getting

java.lang.NoClassDefFoundError: com/android/dx/stock/ProxyBuilder

or

java.lang.ExceptionInInitializerError

when I try to mock a concrete class on dexmaker 1.4 or 1.3. Reverting to 1.2 from google code works fine.

java.lang.NoClassDefFoundError: com/android/dx/stock/ProxyBuilder
at com.android.dx.mockito.DexmakerMockMaker.createMock(DexmakerMockMaker.java:56)
at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:33)
at org.mockito.internal.MockitoCore.mock(MockitoCore.java:59)
at org.mockito.Mockito.mock(Mockito.java:1285)
at org.mockito.Mockito.mock(Mockito.java:1163)
at lt.segfoltas.asthmacontroltest.DbAsyncTests.setUp(DbAsyncTests.java:28)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:190)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:175)
at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:555)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1584)


java.lang.ExceptionInInitializerError
at com.android.dx.mockito.DexmakerMockMaker.createMock(DexmakerMockMaker.java:56)
at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:33)
at org.mockito.internal.MockitoCore.mock(MockitoCore.java:59)
at org.mockito.Mockito.mock(Mockito.java:1285)
at org.mockito.Mockito.mock(Mockito.java:1163)
at lt.segfoltas.asthmacontroltest.DbAsyncTests.setUp(DbAsyncTests.java:28)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:190)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:175)
at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:555)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1584)
Caused by: java.lang.ExceptionInInitializerError
at com.android.dx.stock.ProxyBuilder.<clinit>(ProxyBuilder.java:758)
... 15 more
Caused by: java.lang.NoClassDefFoundError: com.android.dx.rop.type.Type
at com.android.dx.TypeId.<clinit>(TypeId.java:36)
... 16 more

Mocking interfaces works as before. I'd just use 1.2, but I hit #12 on lollipop.

Mocking causes NullPointerException when running under Test Orchestrator

Running tests on a P device with connectedAndroidTest runs fine, but crashes when using the Android Test Orchestrator, throwing the following:

java.lang.NullPointerException: Attempt to invoke interface method 'boolean org.mockito.plugins.MockMaker$TypeMockability.mockable()' on a null object reference
at org.mockito.internal.util.MockCreationValidator.validateType(MockCreationValidator.java:23)
at org.mockito.internal.creation.MockSettingsImpl.validatedSettings(MockSettingsImpl.java:238)
at org.mockito.internal.creation.MockSettingsImpl.build(MockSettingsImpl.java:226)
at org.mockito.internal.MockitoCore.mock(MockitoCore.java:68)
at org.mockito.Mockito.mock(Mockito.java:1895)
at org.mockito.Mockito.mock(Mockito.java:1804)
...

In my case, I am using the Test Orchestrator for running on Firebase.

dexmaker proxy abstract class result in dex2oat failure,phone:HMNOTE2_V7.0.6.0.LHMCNCI

we encounter this question when release apk to app market.

the problem was that dexmaker generated proxy class contains call to super abstract method, which fail the dex2oat tool in MIUI V7.0.6.0.LHMCNCI.

workaround is :
Since build() method checks InvocationHandler not null, and we use InvocationHandler when proxy our class, generated class don’t need call super.XXX if XXX is abstract in baseClass.

dexmaker will generate class like below:(clone method is just an EXAMPLE)

public Object clone() {
Method method = $__methodArray[0];
Object[] objArr = new Object[0];
InvocationHandler invocationHandler = this.$__handler;
if (null == invocationHandler) {
return super.clone();
}
return invocationHandler.invoke(this, method, objArr);
}

Our workaround generated class is like this:

public Object clone() {
    Method method = $__methodArray[0];
    Object[] objArr = new Object[0];
    InvocationHandler invocationHandler = this.$__handler;
    if (null != invocationHandler) {
        return invocationHandler.invoke(this, method, objArr);
    }
    throw new IllegalStateException();
}

Patch file is attached.

0001-dexmaker-abstract-super-call-abstract-method.txt

dexcache == null error

This issue from the old google code project: https://code.google.com/p/dexmaker/issues/detail?id=2 is still valid as of the released 1.2 version

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:51)
at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:41)
...

However, I verified that it has been fixed in the current github repository (last commit March 12th 2015 - ca74669):

  • git clone https://github.com/crittercism/dexmaker.git
  • cd dexmaker
  • mvn install -Dmaven.test.skip=true
  • cp -R ~/.m2/repository/com/google/dexmaker $ANDROID_HOME/extras/android/m2repository/com/google
  • and then update project dependency to 1.3-SNAPSHOT

It would be nice to have a 1.3 release so that we can take the workaround lines out of the test setUp() methods:
System.setProperty("dexmaker.dexcache", getContext().getCacheDir().getPath());

Thanks!

Unable to run tests with Android and 1.3

Hello,
I'm trying to use version 1.3 (from central repository) with Android and Mockito but I am getting the following error:
Caused by: java.lang.NoClassDefFoundError: com.google.dexmaker.dx.rop.type.Type

I also tried to import also dexmaker-dx 1.3 but it doesn't solve the issue because the package of the included classes is different (com.android.dx instead of com.google.dexmaker.dx).

The only way I've found to make it work is to build v1.3 manually and then include the original-dexmaker-1.3 , dexmaker-dx-1.3 and dexmaker-mockito-1.3 jars in my project.
What's the difference between dexmaker and original-dexmaker? Is there a way to make things work by downloading jars from the central repository instead of including them manually?

java.lang.NoClassDefFoundError: org.mockito.internal.util.MockCreationValidator

Explanation:

Swapping out Google's dexmaker with LinkedIn's dexmaker, I am getting the following error. From what I can tell, the LinkedIn version of the dexmaker is so much larger that it causes the Android Test APK to become Multidex.

I have tried the following: https://stackoverflow.com/questions/30081386/how-to-put-specific-classes-into-main-dex-file and https://stackoverflow.com/questions/34310259/trying-to-test-an-android-module-in-multidex-app-com-android-test-runner-multid.

Any way to avoid this?

Error:

D/AndroidRuntime( 2960): Shutting down VM
    E/AndroidRuntime( 2970): FATAL EXCEPTION: main
    E/AndroidRuntime( 2970): Process: <>, PID: 2970
    E/AndroidRuntime( 2970): java.lang.NoClassDefFoundError: org.mockito.internal.util.MockCreationValidator
    E/AndroidRuntime( 2970): 	at org.mockito.internal.creation.MockSettingsImpl.validatedSettings(MockSettingsImpl.java:166)
    E/AndroidRuntime( 2970): 	at org.mockito.internal.creation.MockSettingsImpl.confirm(MockSettingsImpl.java:162)
    E/AndroidRuntime( 2970): 	at org.mockito.internal.MockitoCore.mock(MockitoCore.java:62)
    E/AndroidRuntime( 2970): 	at org.mockito.Mockito.mock(Mockito.java:1637)
    E/AndroidRuntime( 2970): 	at org.mockito.Mockito.mock(Mockito.java:1550)
    ...
    E/AndroidRuntime( 2970): 	at dagger.internal.DoubleCheck.get(DoubleCheck.java:47)
    ...
    E/AndroidRuntime( 2970): 	at dagger.internal.DoubleCheck.get(DoubleCheck.java:47)
    ...
    E/AndroidRuntime( 2970): 	at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1007)
    E/AndroidRuntime( 2970): 	at android.support.test.runner.MonitoringInstrumentation.callApplicationOnCreate(MonitoringInstrumentation.java:323)
    E/AndroidRuntime( 2970): 	at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4344)
    E/AndroidRuntime( 2970): 	at android.app.ActivityThread.access$1500(ActivityThread.java:135)
    E/AndroidRuntime( 2970): 	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1256)
    E/AndroidRuntime( 2970): 	at android.os.Handler.dispatchMessage(Handler.java:102)
    E/AndroidRuntime( 2970): 	at android.os.Looper.loop(Looper.java:136)
    E/AndroidRuntime( 2970): 	at android.app.ActivityThread.main(ActivityThread.java:5017)
    E/AndroidRuntime( 2970): 	at java.lang.reflect.Method.invokeNative(Native Method)
    E/AndroidRuntime( 2970): 	at java.lang.reflect.Method.invoke(Method.java:515)
    E/AndroidRuntime( 2970): 	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
    E/AndroidRuntime( 2970): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
    E/AndroidRuntime( 2970): 	at dalvik.system.NativeStart.main(Native Method)

Not implementing interface due to wrong cache

Yesterday I faced a problem where it happened:

                if (EntityDao.class.isAssignableFrom(daoClass)) {
                    builder = ProxyBuilder.forClass(JAPTSYAndroidBaseEntityDao.class);
                } else {
                    builder = ProxyBuilder.forClass(JAPTSYAndroidBaseDao.class);
                }
                builder.implementing(daoClass)
                        .constructorArgTypes(...)
                        .constructorArgValues(...);
                    builder.dexCache(DexCacheUtil.getDexCacheDir(appContext));
                builder.handler(handler);
                generatedDao = (D) builder.build();

                if (!daoClass.isAssignableFrom(generatedDao.getClass())) {
                           //Bug!
                            Log.v("oh oh");
                 }

I have the following scenario:

public interface ShoppingListElementDao extends EntityDao<ShoppingListElement, ShoppingListElementId, EnParejaDaoFactory> {
}

public interface HomeTaskDao extends EntityDao<HomeTask, HomeTaskId, EnParejaDaoFactory> {
}

public abstract class JAPTSYAndroidBaseEntityDao<T, I, D extends IsDaoFactory> extends JAPTSYAndroidBaseDao<D>
        implements EntityDao<T, I, D> {
}

I'm running the code above twice, one for each interface. With the first interface, it works properly but with the second (no matter the order) the implementing class is not properly being implemented.

Turns out that if I clear the dexCache directory when I detect that issue, it works. I have made this workaround:

    //Bug
    if (!bugDetected && !daoClass.isAssignableFrom(generatedDao.getClass())) {
                //Bug!
                Log.v("oh oh");
        DexCacheUtil.clearDexCacheDir(appContext);
        bugDetected = true;
    } else {
        bugDetected = false;
    }
} while (bugDetected);

But this workaround is very annoying because time regenerating cache dir again takes too long.

Focusing the issue, it seems like the problem comes when both the interfaces and the superclass share same interface (in my case EntityDao) so it believes it's same cached class but it's not.

Thanks in advance.

Proposal: DexMaker. generateFileName() should take into account method accessibility

I just hit a bug which I believe to be the result of changing the accessibility of method on a class from protected to public. I had a spy() on an instance of the class.

DexMaker.generateFileName() generates a hash using (among other things) MethodId. MethodId doesn't include method accessibility (public vs protected) so the cached key of generated code was the same after I made the change.

Therefore, the dexcache continued to pick out the dex generated back when the method was protected.

This had knock-ons elsewhere for me. (For some reason) we have a local patch in Android in ProxyBuilder.getMethodsToProxyRecursive(). This change sorts the methods by Method.toString() (which does include the access modifier). It means the order of $__methodsArray changed.

The upshot was that the cached generated code was indexing into $__methodsArray with different indexes from the ones used at runtime.

This local patch was the immediate cause of my issue, but I propose that generateFileName() should be a bit more conservative when generating the cache key. For example, any change to method signatures that affects the ordering of ProxyBuilder.getMethodsToProxyRecursive() but does not result in a different hash in generateFileName() could cause similar issues.

Even if the ordering of ProxyBuilder.getMethodsToProxyRecursive() were stable in the face of access modifier changes, the generated code pulled out of the cache would have had incorrect access modifiers so it is probably incorrect to treat this as a cache hit.

How to proxy anonymous inner class?

cannot proxy inaccessible class class com.leanplum.testproj.MessagesListActivity$1MyListAdapter

Getting this error, when trying to run:

proxyAdapterClass = ProxyBuilder.forClass(existingListAdapter.getClass())
     .dexCache(activity.getDir("leanplumClassDir", Context.MODE_PRIVATE))
     .handler(handler)
     .build();

Looks like proxying anonymous inner classes is not supported by dexmaker, is it?

Support serialization of Mockito mocks

I'm writing a unit-test for Android app, which creates a mock of serializable custom class and tries to serialize it. It works fine in JUnit test when I'm running it in JVM, but fails with following error when I'm running it as Android test in emulator:

Full gist of error message.

W/System.err: java.io.NotSerializableException: com.android.dx.mockito.InvocationHandlerAdapter
W/System.err:     at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1224)
W/System.err:     at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1584)
W/System.err:     at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1549)
W/System.err:     at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1472)

Here is my code (Android test gist):

Entity serializableMock = mock(Entity.class, Mockito.withSettings().serializable());
try {
    ObjectOutputStream out = new ObjectOutputStream(new ByteArrayOutputStream());
    out.writeObject(serializableMock);
} catch (IOException e) {//catching NotSerializableException here} 

Entity:

package org.test;
public class Entity implements Serializable {
    public String str;
    // getters/setters
}

Gradle dependencies:

androidTestCompile 'org.mockito:mockito-core:2.4.0'
androidTestCompile 'com.linkedin.dexmaker:dexmaker-mockito:2.2.0'
androidTestCompile "com.android.support:support-annotations:25.0.0"
androidTestCompile 'com.android.support.test:runner:0.5'
androidTestCompile 'com.android.support.test:rules:0.5'

How to properly serialize mock object on Android?

Create a new release

Could you publish a new release? Quite some commits were introduced since the last one, and I mostly interested in 1e417ef so that I can remove my fork. Thanks!

SIGSEGV crashes when using dexmaker-mockito-inline

Hi! I'm having strange random crashes when I use mocks with dexmaker-mockito-inline.
It happens randomly but it consistently happens with some probability when I use mocks. When I use mocks it crashes sometimes and without mocks it doesn't crash in 100% cases so I'm pretty sure it's related to runtime instrumentation that is done by dexmaker-mockito-inline.

Here is an excerpt from the crash log:

2019-10-19 14:42:30.551 9633-9646/com.example.app.test A/libc: Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x8 in tid 9646 (HeapTaskDaemon), pid 9633 (ncomponent.test)

2019-10-19 14:42:30.753 9698-9698/? A/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
2019-10-19 14:42:30.753 9698-9698/? A/DEBUG: Build fingerprint: 'google/sdk_gphone_x86/generic_x86:10/QSR1.190920.001/5891938:userdebug/dev-keys'
2019-10-19 14:42:30.753 9698-9698/? A/DEBUG: Revision: '0'
2019-10-19 14:42:30.753 9698-9698/? A/DEBUG: ABI: 'x86'
2019-10-19 14:42:30.754 9698-9698/? A/DEBUG: Timestamp: 2019-10-19 14:42:30+0100
2019-10-19 14:42:30.754 9698-9698/? A/DEBUG: pid: 9633, tid: 9646, name: HeapTaskDaemon  >>> com.example.app.test <<<
2019-10-19 14:42:30.754 9698-9698/? A/DEBUG: uid: 10134
2019-10-19 14:42:30.754 9698-9698/? A/DEBUG: signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x8
2019-10-19 14:42:30.754 9698-9698/? A/DEBUG: Cause: null pointer dereference
2019-10-19 14:42:30.754 9698-9698/? A/DEBUG:     eax e0d60100  ebx f2c74a74  ecx 00000000  edx 00000001
2019-10-19 14:42:30.754 9698-9698/? A/DEBUG:     edi ec293fe4  esi ec293fe0
2019-10-19 14:42:30.754 9698-9698/? A/DEBUG:     ebp ea114a58  esp ea114a00  eip f27e1f8e

I can't find any backtrace or tombstone and looks like the crash is happening in HeapTaskDaemon. Does anyone have any ideas why is this happening or how can I debug this issue?

Full crash log:
https://gist.github.com/lukaville/955fffb4b11f882668141e0355d8ec0d

I've tested on different Android versions and it is reproducible on Android 9, 10

Allow mocking of static methods

@mockitoguy, @drewhannay

The Android API uses static methods, e.g. in Settings.Global . Some tests might want to mock those.

In mockito/mockito#1013 there is a discussion for adding that to mockito. Unfortunately it might take some time to get this into mockito as we need

  • a (non-Android) JVM implementation
  • Agree on and API and get it approved

Hence I am proposing to prototype this as a variant of dexmaker-mockito-inline. Once this has proved useful we can upstream it to mockito.

I propose the following agena:

  1. Add a new sub-project dexmaker-mockito-inline-extended. Prototype
  2. Wrap the Mockito class and add static mocking. Proposal by @mockitoguy, Full java-doc of Proposal
  3. The new APIs need to be clearly marked as "unstable"
  4. Ship this extended mockito and get feedback
  5. Work with mockito to upstream the new APIs

AppDataDirGuesses incorrectly guesses data directory for secondary Android users

"/data/data" directory is a data directory for user 0. If there are multiple users on the device, each will have a separate data dir indicated by their user id in "/data/user/[user_id]" i.e.
"/data/user/0" -> "data/data" (user 0 directory)
"/data/user/10" (user 10 directory)

When we run a test in a secondary user (like user 10), AppDataDirGuesser.guess() will return null, and the test will fail unless "dexmaker.dexcache" has been explicitly specified.

To fix this:

  • We would need to guess the ID of the user running the test. This can be fetched through android APIs, like android.os.Process, but dexmaker doesn't depend on the android library.
  • Try to find a directory by iterating over possible user IDs, capping at some reasonable maximum. This is hacky and limited, but might be the only option if we can't depend on android

Cannot inject mocks when using dexmaker-mockito-inline

Hi i have an issue using dexmaker-mockito-inline from within android studio.

I want to use it in order to mock some final methods of a class but before doing that i try to run some tests that were previously successfully run on target when i was using mockito-android.

I run my test class with MockitoJUnitRunner and i use mock annotations like @mock and @Injectmocks
and i seem to have issues when initMocks() is called:

The stacktrace is the following:

Started running tests

java.lang.NullPointerException: Attempt to invoke interface method 'boolean org.mockito.plugins.MockMaker$TypeMockability.mockable()' on a null object reference
at org.mockito.internal.util.MockCreationValidator.validateType(MockCreationValidator.java:23)
at org.mockito.internal.creation.MockSettingsImpl.validatedSettings(MockSettingsImpl.java:232)
at org.mockito.internal.creation.MockSettingsImpl.build(MockSettingsImpl.java:226)
at org.mockito.internal.MockitoCore.mock(MockitoCore.java:64)
at org.mockito.Mockito.mock(Mockito.java:1871)
at org.mockito.internal.configuration.MockAnnotationProcessor.process(MockAnnotationProcessor.java:36)
at org.mockito.internal.configuration.MockAnnotationProcessor.process(MockAnnotationProcessor.java:16)
at org.mockito.internal.configuration.IndependentAnnotationEngine.createMockFor(IndependentAnnotationEngine.java:38)
at org.mockito.internal.configuration.IndependentAnnotationEngine.process(IndependentAnnotationEngine.java:62)
at org.mockito.internal.configuration.InjectingAnnotationEngine.processIndependentAnnotations(InjectingAnnotationEngine.java:57)
at org.mockito.internal.configuration.InjectingAnnotationEngine.process(InjectingAnnotationEngine.java:41)
at org.mockito.MockitoAnnotations.initMocks(MockitoAnnotations.java:69)
at org.mockito.internal.runners.DefaultInternalRunner$1.withBefores(DefaultInternalRunner.java:39)
at org.junit.runners.BlockJUnit4ClassRunner.methodBlock(BlockJUnit4ClassRunner.java:276)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
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.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.mockito.internal.runners.DefaultInternalRunner$1.run(DefaultInternalRunner.java:79)
at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:85)
at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:39)
at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:163)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
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.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at android.support.test.internal.runner.TestExecutor.execute(TestExecutor.java:56)
at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:384)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2145)

Test running failed: Test run failed to complete. Expected 4 tests, received 1

My build.gradle looks like this

apply plugin: 'com.android.application'

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "myAppId"
        minSdkVersion 27
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        targetCompatibility 1.8
        sourceCompatibility 1.8
    }
}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'com.android.support:appcompat-v7:28.0.0-rc02'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test:rules:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    androidTestImplementation 'com.linkedin.dexmaker:dexmaker-mockito-inline:2.19.1'
    implementation project(':my_interface')
}

What could be the cause of it?
Previously there was no issue when i was using

androidTestImplementation 'org.mockito:mockito-core:2.21.0'
androidTestImplementation 'org.mockito:mockito-android:2.21.0'

Thank you in advance

Example for declare(FieldId<?, ?> fieldId, int flags, Object staticValue)

I would like to create a norma modal with all its fields are public but I can't figure our how to use the provided function declare(FieldId fieldId, int flags, Object staticValue). Can someone please provide me with a working example

I want to create something like that

public class ModelA {
    public String value1;
    public String value2;
    public String value3;
}

My issue is with FieldId value for the declare function.

this.dexMaker.declare(???, Modifier.PUBLIC, "");

ProxyBuilder does not correctly traverse hierarchy for extra interfaces

When using Mockito to mock a class with an extra interface Foo which itself extended interface Bar any attempt to call methods on Bar would result in an AbstractMethodError. e.g.

class Baz {
}

interface Foo extends Bar {
String foo();
}

interface Bar {
int bar();
}

Baz bazMock = mock(Baz.class, withSettings().extraInterfaces(Foo.class))
when(((Foo) bazMock).bar()).thenReturn(4); <-- AbstractMethodError when calling bar().

This is because when ProxyBuilder is collecting the set of methods for which it needs to generate stubs it does not check the interfaces extended by the extra interfaces.

java.lang.VerifyError: SingleControl_Proxy

java.lang.VerifyError: SingleControl_Proxy
at java.lang.reflect.Field.setField(Native Method)
at java.lang.reflect.Field.set(Field.java:585)
at com.google.dexmaker.stock.ProxyBuilder.setMethodsStaticField(ProxyBuilder.java:299)
at com.google.dexmaker.stock.ProxyBuilder.buildProxyClass(ProxyBuilder.java:269)
at com.google.dexmaker.stock.ProxyBuilder.build(ProxyBuilder.java:205)

Can you help me to find why?

inline.extended.ExtendedMockito.mock() sporadically fails on com_android_dx_mockito_inline::InspectClass

In a multi-thread situation there could be native crashes around mocking of static methods (issue #97), even though client doesn't write any native code.

Thread A is running Java_com_android_dx_mockito_inline_StaticMockMethodAdvice_nativeGetCalledClassName() in staticjvmtiagent/agent.cc.
Thread B is the crashing thread that is calling file loaded callbacks to handle classes retransformation, due to an explicit call into Java_com_android_dx_mockito_inline_JvmtiAgent_nativeRetransformClasses() in dexmakerjvmtiagent/agent.cc.

Sequence of events:
(Thread A) Registers file loaded callback using jvmtiEnv->SetEventCallbacks()
(Thread B) JVMTI collects event handler in CollectEvents(), called by DispatchClassFileLoadHookEvent() in events_inl.h
(Thread A) Disposes environment using jvmtiEnv->DisposeEnvironment()
(Thread B) JVMTI into the callback set in Thread A with a disposed jvmtiEnv, and crash.

Does dexmaker work for Art?

Or does it only work for Dalvik? I am particularly interested in using it to proxy public methods in concrete classes.

Support for Powermock

I am trying to incorporate Powermock as a dependency for my Android tests using the following build.gradle configuration:

dependencies{
    compile 'com.android.support:appcompat-v7:21.0.+'
    androidTestCompile('org.mockito:mockito-core:1.9.5')
    androidTestCompile('com.google.dexmaker:dexmaker:1.2')
    androidTestCompile('com.google.dexmaker:dexmaker-mockito:1.2')
    androidTestCompile('org.powermock:powermock-module-junit4:1.5.5') {
        exclude module: 'junit'
    }
    androidTestCompile('org.powermock:powermock-api-mockito:1.5.5') {
        exclude module: 'mockito-all'
    }
}

However, the compiler is complaining that

Error:Gradle: Execution failed for task ':app:packageDebugTest'.
> Duplicate files copied in APK mockito-extensions/org.mockito.plugins.MockMaker
    File 1: ~/.gradle/caches/modules-2/files-2.1/com.google.dexmaker/dexmaker-mockito/1.2/b99884a4c6ef6335ba376f79aa79632b2421c17c/dexmaker-mockito-1.2.jar
    File 2: ~/.gradle/caches/modules-2/files-2.1/com.google.dexmaker/dexmaker-mockito/1.2/b99884a4c6ef6335ba376f79aa79632b2421c17c/dexmaker-mockito-1.2.jar

Looking into the jar structure, I noticed that both Dexmaker and Powermock declare a MockMaker in mockito-extensions
img

What is a MockMaker? How do they differ?
And the most important question: Is it possible to get Powermock to work nicely with Dexmaker?

Thanks in advance. Any help would be greatly appreciated.

Older Android system doesn't have the method call addDexPath.And it's not safe to get current data dir from classloader

A solution for the first is to set the dexElements field of the dexPathList Field in the BaseDexClassLoader.class,It's ok after api 4.
And a better solution for the second is to invoke the static method currentPackageName() in ActivityThread.class. Here's the code:

private static String getPackageName(){
        try {
            if(sCurrentPackageName==null) sCurrentPackageName=Class.forName("android.app.ActivityThread").
                    getDeclaredMethod("currentPackageName");
            if (Build.VERSION.SDK_INT>=18||Looper.getMainLooper()==Looper.myLooper())
                return String.valueOf(sCurrentPackageName.invoke(null));
            else {
                final String[] ret=new String[1];
                Handler handler=new Handler(Looper.getMainLooper(), new Handler.Callback() {
                    @Override
                    public boolean handleMessage(Message msg) {
                        try {
                            ret[0]= (String) sCurrentPackageName.invoke(null);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        synchronized (ret){
                            ret.notify();
                        }
                        return true;
                    }
                });
                handler.sendEmptyMessage(0);
                synchronized (ret){
                    ret.wait();
                }
                return ret[0];
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

ProxyBuilder.getMethodsToProxy() attempts to proxy final method on child class if non-final on superclass

Found in revision: f54f6ea

Encountered while attempting to use Mockito's spy() method to create a real partial mock of a FrameLayout:

FrameLayout parent = spy(new FrameLayout(mActivity));

ProxyBuilder recursively searches for methods starting from the root class, which results in non-final superclass methods being added to the methods-to-override sink. When the search reaches a child class where the method is marked final, it is added to the set of seen final methods; however, it is not removed from the set of methods-to-override.

Incompatible with Mockito 2.10.0

A NoClassDefFoundError occurs because org.mockito.internal.invocation.realmethod.RealMethod does not exist.
https://stackoverflow.com/questions/46161628/noclassdeffounderror-when-updating-from-mockito-2-9-0-to-2-10-0

FYI, org.mockito.invocation.InvocationFactory has been added since 2.10.0. By using it like the code below, you will be able to call MockHandler#handle(Invocation) without relying on org.mockito.internal package.

Invocation invocation = Mockito.framework()
                               .getInvocationFactory()
                               .createInvocation(mockObject,
                                                 mockCreationSettings,
                                                 method,
                                                 realMethodCallable,
                                                 methodParameters);
Object result = mockHandler.handle(invocation);

See the Javadoc.

Related: #64

Avoiding Mockito internal APIs

Hey guys!

Dexmaker is using internal Mockito APIs, are you interested in working together to clean this up?

While working on extending Mockito APIs so that it is easier to integrate (mockito/mockito#1110), I took a look at dexmaker code in case I need to be cautions about breaking some compatibility :) I found a class that imports a couple of internal Mockito classes (org.mockito.internal).

java.lang.ClassNotFoundException: com.android.dex.DexIndexOverflowException

Environment:

Test repo: jaredsburrows/android-gif-search@9a26ca9

Repro steps:

  • When I change Espresso to 3.0 and the Runner to 1.0.0, I get the following error.
  • gradlew connectedDebugAndroidTest

Error:

java.lang.ClassNotFoundException: com.android.dex.DexIndexOverflowException
at java.lang.Class.classForName(Native Method)
at java.lang.Class.forName(Class.java:251)
at android.support.test.internal.runner.TestLoader.doCreateRunner(TestLoader.java:76)
at android.support.test.internal.runner.TestLoader.getRunnersFor(TestLoader.java:104)
at android.support.test.internal.runner.TestRequestBuilder.build(TestRequestBuilder.java:808)
at android.support.test.runner.AndroidJUnitRunner.buildRequest(AndroidJUnitRunner.java:481)
at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:367)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1701)
Caused by: java.lang.NoClassDefFoundError: com/android/dex/DexIndexOverflowException
... 8 more
Caused by: java.lang.IncompatibleClassChangeError: superclass is final
at dalvik.system.DexFile.defineClassNative(Native Method)
at dalvik.system.DexFile.defineClass(DexFile.java:222)
at dalvik.system.DexFile.loadClassBinaryName(DexFile.java:215)
at dalvik.system.DexPathList.findClass(DexPathList.java:322)
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:54)
at java.lang.ClassLoader.loadClass(ClassLoader.java:497)
at java.lang.ClassLoader.loadClass(ClassLoader.java:457)
... 8 more

Fixes?

The missing class is coming from: https://github.com/JakeWharton/dalvik-dx, a dependency in the project

  • Revert back to Mockito 1 and DexMakerMockito 1?
  • Use stubs and not mocks?

New release

Hi. The last one has been pushed over the year ago. Some nice fixes have been done up to this time. Any chance to have some release in near time?

const-class support

I want to generate a class like this:

public class Generated {
    public static void test() {
        Class<?> clazz = Generated.class; // how to access the generated class's class?
    }
}

I have read the source but i cannot found a way to do operation: Generated.class

How can i do this ?

Which version of dexmaker-mockito supports mocking final in androidTest

Hi guys,

I've been trying to mock the final classes with older version of Mockito - and Dexmaker-Mockito. However, I couldn't get them to work and I am getting several exceptions with different versions and approaches. I've done some digging online and said mocking final classes in Android Instrumentation Tests aren't possible. Is that the case?

Thanks!

java.lang.NoClassDefFoundError: Could not initialize class com.android.dx.stock.ProxyBuilder

dexmaker-mockito-inline build fails with Cannot notify listeners of type ProjectEvaluationListener as these listeners are already being notified.

This only happens when syncing gradle files from inside Android Studio. ./gradlew build works. Downgrading to org.jfrog.buildinfo:build-info-extractor-gradle:4.3.0 causes other failures.

This seems to be jfrog/build-info#86


Error:FAILURE: Build failed with an exception.

  • Where:
    Build file '/usr/local/google/home/moltmann/projects/dexmaker/dexmaker-mockito-inline/build.gradle' line: 66

  • What went wrong:
    A problem occurred configuring project ':dexmaker-mockito-inline'.

Failed to notify project evaluation listener.
A problem occurred configuring project ':dexmaker'.
> Cannot notify listeners of type ProjectEvaluationListener as these listeners are already being notified.
A problem occurred configuring project ':dexmaker'.
> Cannot notify listeners of type ProjectEvaluationListener as these listeners are already being notified.

  • Try:
    Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

  • Get more help at https://help.gradle.org

BUILD FAILED in 0s


@drewhannay Any idea how we can work around this?

Inheritance?

Is it possible to do inheritance with dexmaker?

I would like to create something like:

public class FOO extends BAR {
    public FOO( ) { }
}

I've gotten it working without the extends BAR part. I've read through tests and a little code, couldn't find an obvious way to do it. Is this possible?

MockK framework is using dexmaker

Hi,

I took dexmaker-inline-mockito and dexmaker-inline-static-mockito as a base and built a solution for MockK mocking framework. This is Kotlin framework, which is quite popular and competing with Mockito wrapper for Kotlin.

Here is sources dex maker branch and PR

I have a set of ~300 tests running for Java, so I adpoted around 260 of them to run on dexmaker based version. I fixed few issues. Alike mocking of Exception and HashMap.

What I can admit:

  • Tests are running pretty slow (going to spend some time for optimization)
  • Not sure I understand how convenient for users limitation to Android P
  • I don't know what should I do with License terms and copyrights (should I just keep them?)
  • I don't know yet how to integrate it to continuous integration build (running emulator probably, but not yet touched that)
  • I would need to follow-up all the changes to dexmaker since current commit to keep my project in sync.

Would appreciate any answers from your experience.

Thanks!

Crashes debugger

After using this library, setting breakpoints seems to crash the app. Here's a backtrace:

backtrace:
  #00  pc 00000000001a1864  /system/lib64/libart.so (art::annotations::GetSourceDebugExtension(art::Handle<art::mirror::Class>)+96)
  #01  pc 00000000001722cc  /system/lib64/libart.so (art::Dbg::GetSourceDebugExtension(unsigned long, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>*)+284)
  #02  pc 000000000030f02c  /system/lib64/libart.so (art::JDWP::RT_SourceDebugExtension(art::JDWP::JdwpState*, art::JDWP::Request*, art::JDWP::ExpandBuf*)+56)
  #03  pc 000000000030d408  /system/lib64/libart.so (art::JDWP::JdwpState::ProcessRequest(art::JDWP::Request*, art::JDWP::ExpandBuf*, bool*)+772)
  #04  pc 0000000000314358  /system/lib64/libart.so (art::JDWP::JdwpState::HandlePacket()+164)
  #05  pc 000000000055e728  /system/lib64/libart.so (art::JDWP::JdwpAdbState::ProcessIncoming()+928)
  #06  pc 00000000003146a0  /system/lib64/libart.so (art::JDWP::JdwpState::Run()+448)
  #07  pc 0000000000313d74  /system/lib64/libart.so (art::JDWP::StartJdwpThread(void*)+40)
  #08  pc 0000000000069c78  /system/lib64/libc.so (__pthread_start(void*)+36)
  #09  pc 000000000001eeb8  /system/lib64/libc.so (__start_thread+68)

Is it possible to use dexmaker-mockito-inline on API <28?

Hi,
I need dexmaker-mockito-inline to run tests on Android 9.0, but I still want to run tests on older apis. Currently I have this error:
Caused by: java.io.IOException: Requires API level 28. API level is 23

Is there a way to run tests on different API without changing gradle dependencies which is not quite convenient?

Fix transitive dependencies

As it stands, 3 dependencies are required to properly support Mockito:

androidTestCompile "com.crittercism.dexmaker:dexmaker:1.4"
androidTestCompile "com.crittercism.dexmaker:dexmaker-mockito:1.4"
androidTestCompile "com.crittercism.dexmaker:dexmaker-dx:1.4"

dexmaker-mockito doesn't work without the other two modules. Shouldn't it have transitive dependencies on them?

If the dexmaker module also depends on dexmaker-dx, it ought to have a transitive dependency too. (I'm not sure if this is the case though since I have no use for this particular configuration.)

Multidex incompatible class change error

Hi,

I'm working on an application that uses dexmaker-mockito:2.2.0 in its instrumentation tests. I recently crossed the dex limit so I integrated multidex to continue development. After multidex was integrated I began seeing the error below when calling Mockito.spy:

java.lang.IncompatibleClassChangeError: Class 'com.android.dx.util.ByteArrayAnnotatedOutput' does not implement interface 'com.android.dex.util.ByteOutput'
in call to 'void com.android.dex.util.ByteOutput.writeByte(int)'
(declaration of 'com.android.dex.Leb128' appears in /data/app/com.example.test-2/base.apk:classes12.dex)
at com.android.dex.Leb128.writeUnsignedLeb128(Leb128.java:140)
at com.android.dx.util.ByteArrayAnnotatedOutput.writeUleb128(ByteArrayAnnotatedOutput.java:244)
at com.android.dx.dex.file.ClassDataItem.encodeSize(ClassDataItem.java:378)
at com.android.dx.dex.file.ClassDataItem.encodeOutput(ClassDataItem.java:347)
at com.android.dx.dex.file.ClassDataItem.place0(ClassDataItem.java:328)
at com.android.dx.dex.file.OffsettedItem.place(OffsettedItem.java:242)
at com.android.dx.dex.file.MixedItemSection.placeItems(MixedItemSection.java:311)
at com.android.dx.dex.file.DexFile.toDex0(DexFile.java:541)
at com.android.dx.dex.file.DexFile.toDex(DexFile.java:214)
at com.android.dx.DexMaker.generate(DexMaker.java:324)
at com.android.dx.DexMaker.generateAndLoad(DexMaker.java:422)
at com.android.dx.stock.ProxyBuilder.buildProxyClass(ProxyBuilder.java:266)
at com.android.dx.mockito.DexmakerMockMaker.createMock(DexmakerMockMaker.java:60)
at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:35)
at org.mockito.internal.MockitoCore.mock(MockitoCore.java:63)
at org.mockito.Mockito.mock(Mockito.java:1637)
at com.example.application.test.utils.MockHelper.mock(MockHelper.java:23)
at com.example.application.arch.testmodules.TestUtilsModule.providesPhoneNumberResolver(TestUtilsModule.java:72)
at com.example.application.arch.testmodules.TestUtilsModule_ProvidesPhoneNumberResolverFactory.get(TestUtilsModule_ProvidesPhoneNumberResolverFactory.java:38)
at com.example.application.arch.testmodules.TestUtilsModule_ProvidesPhoneNumberResolverFactory.get(TestUtilsModule_ProvidesPhoneNumberResolverFactory.java:11)
at dagger.internal.DoubleCheck.get(DoubleCheck.java:47)
at com.example.application.test.exampleRootModule_MembersInjector.injectMembers(exampleRootModule_MembersInjector.java:289)
at com.example.application.test.exampleRootModule_MembersInjector.injectMembers(exampleRootModule_MembersInjector.java:38)
at com.example.application.arch.DaggerTestComponent.inject(DaggerTestComponent.java:2680)
at com.example.application.test.exampleRootModule.onInitialize(exampleRootModule.java:392)
at com.example.runner.Core.start(Core.java:213)
at com.example.runner.Runner.onStart(Runner.java:53)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1879)

The errors appears to be caused by a mismatch between com.android.dex.Leb128 and com.android.dx.util.ByteArrayAnnotatedOutput. ByteArrayAnnotatedOutput isn't included from any other library and the Mockito version is the one specified by dexmaker.

Interestingly, only some instances of Mockito.mock have the above issue. The code diverges at DexMaker:190:

File result1 = new File(dexCache, this.generateFileName());

For instances where the mock is correctly loaded, result1 exists. For instances where the mock encounters the above error, result1 does not exist.

I thought the problem might have been due to the class not being packaged in the main dex file, but adding the class to the multidex keep file didn't fix the issue.

Unfortunately, I can't provide you with a minimal working example because the exact cause of the problem is hard to nail down. However, I'm interested to get some insight into how an IncompatibleClassChangeError might occur in this scenario.

Thanks,

Andrew

Suggestion : Improvement of String.equals("xxx")

Hi,
I have found some usage of “String.equals("xxx")” in this project.

When using string.equals("a") ,it will get nullpointexception if string ==null.
The problem can be fixed by repalced with "a".equals(string) or Objects.equals(string,"a").

Detail websites and lines are listed below

71 77 | https://github.com/linkedin/dexmaker/blob/master/dexmaker-mockito/src/main/java/com/android/dx/mockito/InvocationHandlerAdapter.java
43 49 | https://github.com/linkedin/dexmaker/blob/master/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/InvocationHandlerAdapter.java

Best regards

Release v1.3

Hi @dshirley, I'm curious what the timeline might be on releasing the next version of dexmaker? I saw some activity a little over a week ago, but there hasn't been any update since then. I know you're busy, and I appreciate your time. Get back to me when you can.

Thanks!

License

Hey, could you please add a license to the project? I noticed on the google code you have apache v2, are you still using this?

Could not initialize plugin: interface org.mockito.plugins.MockMaker

Hi Team,
Getting the below mentioned error when included the
androidTestImplementation 'com.linkedin.dexmaker:dexmaker-mockito-inline:2.16.0' in the build.gradle.

The cause shows that net.bytebuddy.matcher.ElementMatchers is not found, which was removed explicitly in the release.
Below is the stack trace.
java.lang.IllegalStateException: Could not initialize plugin: interface org.mockito.plugins.MockMaker (alternate: null)
at org.mockito.internal.configuration.plugins.PluginLoader$1.invoke(PluginLoader.java:74)
at java.lang.reflect.Proxy.invoke(Proxy.java:1006)
at $Proxy7.isTypeMockable(Unknown Source)
at org.mockito.internal.util.MockUtil.typeMockabilityOf(MockUtil.java:29)
at org.mockito.internal.util.MockCreationValidator.validateType(MockCreationValidator.java:22)
at org.mockito.internal.creation.MockSettingsImpl.validatedSettings(MockSettingsImpl.java:232)
at org.mockito.internal.creation.MockSettingsImpl.build(MockSettingsImpl.java:226)
at org.mockito.internal.MockitoCore.mock(MockitoCore.java:64)
at org.mockito.Mockito.mock(Mockito.java:1864)
at org.mockito.Mockito.mock(Mockito.java:1777)
at com.xyz.example.ui.base.modules.CommanApiTestModule.provideEndpoint(CommanApiTestModule.java:35)
at com.xyz.example.ui.base.modules.CommanApiTestModule_ProvideEndpointFactory.get(CommanApiTestModule_ProvideEndpointFactory.java:19)
at com.xyz.example.ui.base.modules.CommanApiTestModule_ProvideEndpointFactory.get(CommanApiTestModule_ProvideEndpointFactory.java:8)
at dagger.internal.DoubleCheck.get(DoubleCheck.java:47)
at com.xyz.example.ui.base.modules.CommanApiTestModule_ProvideRetrofitFactory.get(CommanApiTestModule_ProvideRetrofitFactory.java:46)
at com.xyz.example.ui.base.modules.CommanApiTestModule_ProvideRetrofitFactory.get(CommanApiTestModule_ProvideRetrofitFactory.java:13)
at dagger.internal.DoubleCheck.get(DoubleCheck.java:47)
at com.xyz.example.ui.base.modules.seeker.ApiTestModule_ProvidesNetworkServiceFactory.get(ApiTestModule_ProvidesNetworkServiceFactory.java:26)
at com.xyz.example.ui.base.modules.seeker.ApiTestModule_ProvidesNetworkServiceFactory.get(ApiTestModule_ProvidesNetworkServiceFactory.java:10)
at dagger.internal.DoubleCheck.get(DoubleCheck.java:47)
at com.xyz.example.ui.base.modules.seeker.ApiTestModule_ProvidesServiceFactory.get(ApiTestModule_ProvidesServiceFactory.java:26)
at com.xyz.example.ui.base.modules.seeker.ApiTestModule_ProvidesServiceFactory.get(ApiTestModule_ProvidesServiceFactory.java:10)
at dagger.internal.DoubleCheck.get(DoubleCheck.java:47)
at com.xyz.exampleseeker.ui.login.OtpPresenter_MembersInjector.injectMembers(OtpPresenter_MembersInjector.java:35)
at com.xyz.exampleseeker.ui.login.OtpPresenter_MembersInjector.injectMembers(OtpPresenter_MembersInjector.java:9)
at dagger.internal.MembersInjectors.injectMembers(MembersInjectors.java:37)
at com.xyz.exampleseeker.ui.login.OtpPresenter_Factory.get(OtpPresenter_Factory.java:18)
at com.xyz.exampleseeker.ui.login.OtpPresenter_Factory.get(OtpPresenter_Factory.java:8)
at com.xyz.example.ui.base.modules.seeker.LoginTestModule_ProvideOtpPresenterFactory.get(LoginTestModule_ProvideOtpPresenterFactory.java:27)
at com.xyz.example.ui.base.modules.seeker.LoginTestModule_ProvideOtpPresenterFactory.get(LoginTestModule_ProvideOtpPresenterFactory.java:10)
at dagger.internal.DoubleCheck.get(DoubleCheck.java:47)
at com.xyz.example.ui.login.OtpFragmentInstrumentationTest_MembersInjector.injectMembers(OtpFragmentInstrumentationTest_MembersInjector.java:36)
at com.xyz.example.ui.login.OtpFragmentInstrumentationTest_MembersInjector.injectMembers(OtpFragmentInstrumentationTest_MembersInjector.java:9)
at com.xyz.example.ui.base.components.DaggerSeekerAppTestComponent$LoginTestSubComponentImpl.inject(DaggerSeekerAppTestComponent.java:1567)
at com.xyz.example.ui.login.OtpFragmentInstrumentationTest.setUp(OtpFragmentInstrumentationTest.java:56)
at java.lang.reflect.Method.invoke(Native Method)
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 android.support.test.internal.runner.junit4.statement.RunBefores.evaluate(RunBefores.java:76)
at android.support.test.rule.ActivityTestRule$ActivityStatement.evaluate(ActivityTestRule.java:433)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
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.junit.runners.ParentRunner.run(ParentRunner.java:363)
at android.support.test.runner.AndroidJUnit4.run(AndroidJUnit4.java:101)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
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.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at android.support.test.internal.runner.TestExecutor.execute(TestExecutor.java:56)
at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:385)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2136)
Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lnet/bytebuddy/matcher/ElementMatchers;
at org.mockito.internal.creation.bytebuddy.SubclassBytecodeGenerator.(SubclassBytecodeGenerator.java:54)
at org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker.(SubclassByteBuddyMockMaker.java:37)
at org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker.(SubclassByteBuddyMockMaker.java:33)
at org.mockito.internal.creation.bytebuddy.ByteBuddyMockMaker.(ByteBuddyMockMaker.java:21)
at java.lang.Class.newInstance(Native Method)
at org.mockito.internal.configuration.plugins.DefaultMockitoPlugins.create(DefaultMockitoPlugins.java:66)
at org.mockito.internal.configuration.plugins.DefaultMockitoPlugins.getDefaultPlugin(DefaultMockitoPlugins.java:43)
at org.mockito.internal.configuration.plugins.PluginLoader.loadPlugin(PluginLoader.java:67)
at org.mockito.internal.configuration.plugins.PluginLoader.loadPlugin(PluginLoader.java:44)
at org.mockito.internal.configuration.plugins.PluginRegistry.(PluginRegistry.java:21)
at org.mockito.internal.configuration.plugins.Plugins.(Plugins.java:18)
at org.mockito.internal.configuration.plugins.Plugins.getMockMaker(Plugins.java:34)
at org.mockito.internal.util.MockUtil.(MockUtil.java:24)
... 62 more
Caused by: java.lang.ClassNotFoundException: Didn't find class "net.bytebuddy.matcher.ElementMatchers" on path: DexPathList[[zip file "/system/framework/android.test.runner.jar", zip file "/system/framework/android.test.mock.jar", zip file "/data/app/com.xyz.example.debug.test-JPbCQCwit0DQb7CwHpsH_A==/base.apk", zip file "/data/app/com.xyz.example.debug-7eFx6EDFhN6rL1u6Z4hqVg==/base.apk"],nativeLibraryDirectories=[/data/app/com.xyz.example.debug.test-JPbCQCwit0DQb7CwHpsH_A==/lib/x86, /data/app/com.xyz.example.debug-7eFx6EDFhN6rL1u6Z4hqVg==/lib/x86, /system/lib]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:125)
at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
... 75 more

dexmaker-mockito 1.5 release

Looking at the code, I see that the null pointer exception in dexmaker-mockito 1.4's DexmakerMockMaker.getInvocationHandlerAdapter has been fixed since September (17cc9cc). Is there any ETA on a release? I can build myself, but I'd prefer to not have to do so.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.