Hot questions for Using Mockito in android gradle plugin

Hot questions for Using Mockito in android gradle plugin

Top 10 Java Open Source / Mockito / android gradle plugin

Question:

I am using Android Studio 1.2 and the com.android.tools.build:gradle:1.2.2 plugin.

Attempt 1

I include the following in my app/build.gradle:

androidTestCompile ('com.google.dexmaker:dexmaker-mockito:1.2')
androidTestCompile ('org.powermock:powermock-mockito-release-full:1.6.2')

but then the PowerMockito package in not available for import:

error: cannot find symbol
    PowerMockito.mockStatic(DatastoreFactory.class);
    ^
Attempt 2

I include the following in my app/build.gradle:

androidTestCompile ('org.powermock:powermock-api-mockito:1.6.2') {
    exclude module: 'hamcrest-core'
    exclude module: 'objenesis'
}

androidTestCompile ('org.powermock:powermock-module-junit4:1.6.2') {
    exclude module: 'hamcrest-core'
    exclude module: 'objenesis'
}

which is a trial-and-error offshoot of this q/a here: AndroidStudio/Gradle with powermock

This compiles but when run Mockito gives a runtime error:

java.lang.VerifyError: org/mockito/cglib/core/ReflectUtils
    at org.mockito.cglib.core.KeyFactory$Generator.generateClass(KeyFactory.java:167)
    at org.mockito.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
    at org.mockito.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:217)
    at org.mockito.cglib.core.KeyFactory$Generator.create(KeyFactory.java:145)
    at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:117)
    at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:109)
    at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:105)
    at org.mockito.cglib.proxy.Enhancer.<clinit>(Enhancer.java:70)
    at org.powermock.api.mockito.repackaged.ClassImposterizer.createProxyClass(ClassImposterizer.java:95)
    at org.powermock.api.mockito.repackaged.ClassImposterizer.imposterise(ClassImposterizer.java:57)
    at org.powermock.api.mockito.repackaged.ClassImposterizer.imposterise(ClassImposterizer.java:49)
    at org.powermock.api.mockito.repackaged.CglibMockMaker.createMock(CglibMockMaker.java:24)
    at org.powermock.api.mockito.internal.mockmaker.PowerMockMaker.createMock(PowerMockMaker.java:45)
    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 com.mdsol.naga.FormPusherTest.setUp(FormPusherTest.java:40)
    at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:191)
    at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:176)
    at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:554)
    at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1701)

Is anyone using Powermock successfully with Android Studio 1.2? Please share your build.gradle - thanks!


Answer:

It should be good already. At least in my case its working fine.

dependencies {
    testCompile 'junit:junit:4.12'
    testCompile 'org.mockito:mockito-core:2.0.5-beta'
    testCompile 'com.android.support:support-v4:22.0.0'
    testCompile ('org.powermock:powermock-api-mockito:1.6.2') {
        exclude module: 'hamcrest-core'
        exclude module: 'objenesis'
    }
    testCompile ('org.powermock:powermock-module-junit4:1.6.2') {
        exclude module: 'hamcrest-core'
        exclude module: 'objenesis'
    }
}

Also, remember to select Unit Tests in the Test Artifact section of Build Variants in Android Studio. You need to make sure you are using the right project structure, Gradle version and testCompile.

You can find the article about this here: http://vexdev.com/2015/05/06/unit-testing-android/

And the whole project: https://github.com/vexdev/AndroidUnitTest

Question:

I know there are quite a few questions (and answers) for this topic, but I've tried everything I found in SO and other sites and I haven't found a way to make JaCoCo include coverage for Android tests that use Mockito.

My problem: I want to use JaCoCo to generate code coverage of both Unit Test and Instrumentation Test (androidTest). I'm using Mockito to mock some of the classes. I found a sample in GitHub to use JaCoCo and used it as a starting point.

https://github.com/rafaeltoledo/unified-code-coverage-android

When I run the custom jacocoTestReport task included in that example, the code coverage report is properly generated and code coverage is at 100%. The report includes both unit test and android test. However, that sample is not using Mockito (which I need), so I added the following to app/build.gradle

dependencies {
 ...
 androidTestCompile 'org.mockito:mockito-android:2.10.0'
}

I added a very simple Java class called Util at app/src/main/java/net/rafaeltoledo/coverage/Util.java

public class Util {
    public int anIntMethod() {
        return 0;
    }
}

And added the following simple test to the existing android test at app/src/androidTest/java/net/rafaeltoledo/coverage/MainActivityTest.java

@Test
public void utilMethod() {
    Util util = Mockito.mock(Util.class);
    Mockito.doReturn(10).when(util).anIntMethod();
    assertThat(util.anIntMethod(), is(10));
}

When I run the jacocoTestReport again, code coverage drops to 88% and the report in fact shows the Util class was not covered by my tests, even though I clearly have a test that exercises that class.

(I wanted to add screenshots of the reports but I don't have enough reputation, so here's a link to the coverage report and execution report that shows that both tests were in fact executed)

Versions info: Gradle plug-in: 2.3.3 Jacoco: 0.7.8.201612092310 Android Studio: 2.3.3 Android build tools: 25.0.2

Is this a Jacoco limitation or am I doing something wrong?


Answer:

am I doing something wrong?

Let's put aside Android, because there is IMO clearly something wrong with your expectations/understanding about core thing here - mocking:

even though I clearly have a test that exercises that class.

By

@Test
public void utilMethod() {
    Util util = Mockito.mock(Util.class);
    Mockito.doReturn(10).when(util).anIntMethod();
    assertThat(util.anIntMethod(), is(10));
}

you are not testing anIntMethod, you are testing something that always returns 10, no matter what is actually written in anIntMethod.

Coverage shows what was executed and hence absolutely correct that it is zero for anIntMethod since it is not executed.

Mocking is used to isolate class under test from its dependencies, but not to replace it, otherwise you're not testing real code.

Here is an example of proper usage of mocking:

src/main/java/Util.java:

public class Util {
  int anIntMethod(Dependency d) {
    return d.get();
  }
}
class Dependency {
  int get() {
    return 0;
  }
}

src/test/java/UtilTest.java:

import org.junit.Test;
import org.mockito.Mockito;

import static org.junit.Assert.assertEquals;

public class UtilTest {
  @Test
  public void utilMethod() {
    Dependency d = Mockito.mock(Dependency.class);
    Mockito.doReturn(10).when(d).get();
    assertEquals(10, new Util().anIntMethod(d));
  }
}

build.gradle:

apply plugin: "java"
apply plugin: "jacoco"

repositories {
  mavenCentral()
}

dependencies {
  testCompile "junit:junit:4.12"
  testCompile "org.mockito:mockito-core:2.10.0"
}

And after execution of gradle build jacocoTestReport coverage is

And case of partial mocking:

src/main/java/Util.java:

public class Util {
  int get() {
    return 0;
  }

  int anIntMethod() {
    return get();
  }
}

src/test/java/UtilTest.java:

import org.junit.Test;
import org.mockito.Mockito;

import static org.junit.Assert.assertEquals;

public class UtilTest {
  @Test
  public void utilMethod() {
    Util util = Mockito.mock(
      Util.class,
      Mockito.withSettings().defaultAnswer(Mockito.CALLS_REAL_METHODS)
    );
    Mockito.doReturn(10).when(util).get();
    assertEquals(10, util.anIntMethod());
  }
}

In both cases mocked parts are shown as uncovered and this is correct.

Question:

I'm trying to use Mockito for instrumentation tests, when I add

androidTestCompile 'org.mockito:mockito-core:2.7.22'

I get the following error:

Error:Conflict with dependency 'org.objenesis:objenesis' in project ':app'. 
Resolved versions for app (2.1) and test app (2.5) differ. See 
http://g.co/androidstudio/app-test-app-conflict for details.

I have no clue what's wrong, I have no org.objenesis dependency specified anywhere in my code (guess it comes through mockito).

Every help, appreciated.


Answer:

Try this

configurations.all { resolutionStrategy { force 'org.objenesis:objenesis:2.1' } }

or

Just exclude it in your main project. exclude group: 'org.objenesis'