JaCoCo + Mockito + Android 测试:零覆盖率报告

JaCoCo + Mockito + Android tests: Zero coverage reported

我知道这个主题有很多问题(和答案),但我已经尝试了我在 SO 和其他网站上找到的所有内容,但我还没有找到让 JaCoCo 包含 Android 使用 Mockito 的测试。

我的问题: 我想使用 JaCoCo 生成单元测试和仪器测试 (androidTest) 的代码覆盖率。我正在使用 Mockito 模拟一些 classes。我在 GitHub 中找到了一个使用 JaCoCo 的示例,并将其用作起点。

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

当我 运行 包含在该示例中的自定义 jacocoTestReport 任务时,代码覆盖率报告已正确生成并且代码覆盖率为 100%。该报告包括单元测试和 android 测试。但是,该示例未使用 Mockito(我需要),因此我将以下内容添加到 app/build.gradle

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

我在 app/src/main/java/net/rafaeltoledo/coverage/Util 添加了一个非常简单的 Java class 名为 Util。java

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

并在 app/src/androidTest/java/net/rafaeltoledo/coverage/MainActivityTest 的现有 android 测试中添加了以下简单测试。java

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

当我再次 运行 jacocoTestReport 时,代码覆盖率下降到 88%,报告实际上显示 Util class 没有被我的测试覆盖,尽管我显然有一个测试class 的练习。

(我想添加报告的屏幕截图,但我没有足够的声誉,所以这里有一个 link 到 coverage report and execution report,表明这两个测试实际上都已执行)

版本信息: Gradle插件:2.3.3 雅可可:0.7.8.201612092310 Android 工作室:2.3.3 Android 构建工具:25.0.2

这是 Jacoco 的限制还是我做错了什么?

am I doing something wrong?

让我们搁置一旁 Android,因为 IMO 显然有问题 expectations/understanding 关于这里的核心内容 - 嘲笑:

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

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

你不是在测试 anIntMethod,你是在测试总是 returns 10 的东西,不管 anIntMethod.

中实际写的是什么

覆盖率显示了执行的内容,因此绝对正确的是 anIntMethod 是零,因为它没有被执行。

Mocking 用于将测试下的class与其依赖项隔离开来,而不是替换它,否则你不是在测试真正的代码。

下面是正确使用模拟的示例:

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"
}

执行 gradle build jacocoTestReport 后覆盖范围为

以及部分模拟的情况:

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());
  }
}

在这两种情况下,模拟部分都显示为未覆盖,这是正确的。