Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mockito 2.x on Android Instrumentation Tests with Kotlin is not working #1082

Closed
magnumrocha opened this issue May 14, 2017 · 11 comments
Closed
Labels

Comments

@magnumrocha
Copy link

magnumrocha commented May 14, 2017

Hi guys,

I am current working on a Android project using the Kotlin language, and I’m trying mock some classes on Instrumentation Tests. As everything in Kotlin is final by default, we have the known problem of mock final classes, that only works if we apply the "mock-maker-inline" on file “org.mockito.plugins.MockMaker” inside test resources folder (eg.: src/test/resources/mockito-extensions/).

My mockito dependencies on module grade file are:

compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:1.1.2-3"
...

testCompile "junit:junit:4.12"
testCompile "org.jetbrains.kotlin:kotlin-reflect:1.1.2-3"
testCompile "org.mockito:mockito-core:2.8.9”
testCompile ("com.nhaarman:mockito-kotlin:1.4.0", {
    exclude group: 'org.jetbrains.kotlin'
    exclude group: 'org.mockito'
})

androidTestCompile "org.mockito:mockito-android:2.8.9”
androidTestCompile ("com.nhaarman:mockito-kotlin:1.4.0", {
    exclude group: 'org.jetbrains.kotlin'
    exclude group: 'org.mockito'
})
androidTestCompile ("com.android.support.test.espresso:espresso-core:2.2.2", {
    exclude group: 'com.android.support', module: 'support-annotations'
})
androidTestCompile ("com.android.support.test:runner0.5", {
    exclude group: 'com.android.support', module: 'support-annotations'
})
androidTestCompile ("com.android.support.test:rules:0.5", {
    exclude group: 'com.android.support', module: 'support-annotations'
})

For the local unit tests, the "mock-maker-inline" works properly. But for Android Instrumentations Tests it doesn't works. Reading about it on Mockito documentation, we have:

“Be aware that you cannot use the inline mock maker on Android due to limitations in the Android VM”.

We can’t apply the “mock-maker-inline” for Instrumentation tests.

So, how can we enable Mockito to mock final classes on Android Instrumentations Test ??

@tmurakami
Copy link
Contributor

I think there are two ways.

  1. Kotlin all-open plugin
  2. DexOpener

Kotlin all-open plugin

This plugin makes classes annotated with a specific annotation open without the open keyword.

First, you put these files into your project:

  • app/src/ debug /java/your/app/OpenClass.kt
package your.app

@Target(AnnotationTarget.ANNOTATION_CLASS)
annotation class OpenClass
  • app/src/ debug /java/your/app/OpenClassOnDebug.kt
package your.app

@OpenClass
@Target(AnnotationTarget.CLASS)
annotation class OpenClassOnDebug
  • app/src/ release /java/your/app/OpenClassOnDebug.kt
package your.app

@Target(AnnotationTarget.CLASS)
annotation class OpenClassOnDebug

Second, configure your app/build.gradle as follows:

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version"
    }
}

apply plugin: 'kotlin-allopen'

allOpen {
    annotation 'your.app.OpenClass'
}

Finally, annotate OpenClassOnDebug to the class you want to mock:

@OpenClassOnDebug
class YourClass

Now you can open classes and members only on debug mode.

DexOpener

This library opens classes and members while testing on Android.

To use this library, configure your app/build.gradle as follows:

android {
    defaultConfig {
        minSdkVersion 16 // 16 or higher
        testInstrumentationRunner 'com.github.tmurakami.dexopener.DexOpenerAndroidJUnitRunner'
    }
}

repositories {
    jcenter()
    maven { url 'https://jitpack.io' }
}

dependencies {
    androidTestCompile 'org.mockito:mockito-android:2.8.9'
    androidTestCompile 'com.github.tmurakami:dexopener:0.9.2'
}

@magnumrocha
Copy link
Author

Thank you @tmurakami,

The first solution worked as I wanted.

@jaredsburrows
Copy link

@tmurakami Using your DexOpener plugin:

ava.lang.NoClassDefFoundError: org/mockito/internal/configuration/plugins/Plugins
        at org.mockito.internal.configuration.GlobalConfiguration.tryGetPluginAnnotationEngine(GlobalConfiguration.java:55)

Since Mockito-android is so large, I had to use LinkedIn's dexmaker.

@tmurakami
Copy link
Contributor

What version of DexOpener are you using?
In version 0.9.2, there are several bugs caused by ASMDEX that is an old DEX manipulation library.
Could you try using the latest version 0.10.1?
If the problem still occurs, please file a report in the DexOpener issues.

@jaredsburrows
Copy link

@tmurakami Sure. I am trying that now, but now the test app reaches multidex. Do you multidex for your test apks?

@jaredsburrows
Copy link

@tmurakami When turning on multidex:

Starting 0 tests on emulator-5554 - 4.4.2
Tests on emulator-5554 - 4.4.2 failed: Instrumentation run failed due to 'java.lang.NoClassDefFoundError'

com.android.builder.testing.ConnectedDevice > No tests found.[emulator-5554 - 4.4.2] FAILED 
No tests found. This usually means that your test classes are not in the form that your test runner expects (e.g. don't inherit from TestCase or lack @Test annotations).

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':connectedDebugAndroidTest'.
> There were failing tests. See the report at: file:///Users/<>/repo/android-gif-example/build/reports/androidTests/connected/index.html

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

BUILD FAILED in 6s
94 actionable tasks: 39 executed, 55 up-to-date

@tmurakami
Copy link
Contributor

I'd like to investigate it, so please file a report in the DexOpener issues.

@jaredsburrows
Copy link

@tmurakami done.

@GEllickson
Copy link

Curious, is there anything in the works to allow for easier mocking in Android Instrumentation tests besides the two approached above?

I see the "cannot use the inline mock maker on Android due to limitations", but just wondering if some other workaround will be part of Mockito one day.

@jaredsburrows
Copy link

@GEllickson Honestly, what I have found is that Espresso should only be asserting the views have the proper data and visibility. Id add cases, I have used stubs or local mock data.

@makovkastar
Copy link

The DexOpener library worked great for me, thank you @tmurakami for creating it!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

6 participants