【问题标题】:Android: How to run PIT Mutation Testing with Robolectric?Android:如何使用 Robolectric 运行 PIT 突变测试?
【发布时间】:2013-12-13 00:23:11
【问题描述】:

如何使用Robolectric PIT 来测试Android 应用程序?

使用 Robolectric,您可以在 JVM 中运行 Android 测试。使用 PIT,您可以显示线路覆盖率并进行突变测试。对我来说,可以使用 Eclipse+Plugins,但没有要求。


这是我迄今为止尝试过的:

我有一个 Android 项目,我们称之为 MyProject

我现在想使用Robolectric PIT 在 JVM 中测试 MyProject。因此,我创建了另一个名为 MyTest 的项目并成功运行 Robolectric 测试,正如 robolectric quick start 中所述。这就是my.app.tests.MyActivityTest 的样子:

@RunWith(RobolectricTestRunner.class)
public class MyActivityTest {
    @Test
    public void myTest() throws Exception {
        String appName = new MainActivity().getResources().getString(R.string.app_name);
        Assert.assertEquals(appName, "MyProject");
    }
}

现在是棘手的部分:我想将 PIT 的线路覆盖和突变测试添加到我的 Robolectric 测试中。首先尝试使用Pitclipse - 没有运气。 Pitclipse 似乎还不支持 Eclipse Project Depencies。

所以我的第二次尝试是使用命令行,如PIT quick start 中所述:

首先,我确保我的测试在命令行中使用 Junit 成功运行:

java -cp <classpath> org.junit.runner.JUnitCore my.app.tests.MyActivityTest

&lt;classpath&gt; 包含:junit4、robolectric、MyProject 类文件、MyTest 类文件、android.jar 和其他必要的 android 库。

此 JUnit 测试成功后,我在 PIT 调用中使用了相同的 &lt;classpath&gt;,并在 MyProject 的根路径中执行该调用:

java -cp ../MyTest/bin:../MyTest/libs/*:bin/classes:~/android-sdk-linux/platforms/android-17/android.jar \
    org.pitest.mutationtest.MutationCoverageReport \
    --reportDir ../MyTest/pit-report \
    --targetClasses my.app.* \      # package in MyProject
    --targetTests my.app.tests.* \  # package in MyTest
    --sourceDirs src/

但是,这会导致我在下面发布的异常。我想我需要使用 PIT 的 --excludedClasses 参数排除一些类,但没有提示哪个类可能会导致问题。请注意,MyActivityTest 没有超类,也没有显式构造函数。

java.lang.NullPointerException
ERROR Description [testClass=my.app.tests.MyActivityTest, name=myTest(my.app.tests.MyActivityTest)] -> java.lang.NullPointerException
    at org.pitest.boot.CodeCoverageStore.visitProbes(CodeCoverageStore.java:92)
    at my.app.tests.MyActivityTest.<init>(MyActivityTest.java:22)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:532)
    at org.junit.runners.BlockJUnit4ClassRunner.createTest(BlockJUnit4ClassRunner.java:195)
    at org.robolectric.RobolectricTestRunner$HelperTestRunner.createTest(RobolectricTestRunner.java:647)
    at org.junit.runners.BlockJUnit4ClassRunner$1.runReflectiveCall(BlockJUnit4ClassRunner.java:244)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.BlockJUnit4ClassRunner.methodBlock(BlockJUnit4ClassRunner.java:241)
    at org.robolectric.RobolectricTestRunner$HelperTestRunner.methodBlock(RobolectricTestRunner.java:657)
    at org.robolectric.RobolectricTestRunner$2.evaluate(RobolectricTestRunner.java:227)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:175)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.pitest.junit.adapter.CustomRunnerExecutor.run(CustomRunnerExecutor.java:42)
    at org.pitest.junit.adapter.AdaptedJUnitTestUnit.execute(AdaptedJUnitTestUnit.java:86)
    at org.pitest.coverage.execute.CoverageDecorator.execute(CoverageDecorator.java:50)
    at org.pitest.containers.UnContainer.submit(UnContainer.java:46)
    at org.pitest.Pitest$3.run(Pitest.java:148)
    at java.lang.Thread.run(Thread.java:679)

【问题讨论】:

  • 我会开始使用 maven 插件 - 这是核心 PIT 代码库的一部分。 eclipse插件是第三方的,不太成熟。我不熟悉 Android 开发或 Roboelectric - 但从一个快速的 google 看来,只要将正常的 jvm 字节码写入磁盘的某个地方,它就应该可以与 PIT 一起使用。如果有成功(或失败)请发帖到坑谷歌用户群。
  • @henry 在第二次尝试中,我尝试从命令行使用 PIT。普通的 JUnit 测试(不使用 robolectric)与 PIT 配合得很好。但是,使用 robolectric 的 MyActivityTest 会产生上面发布的异常。你知道问题可能是什么吗?
  • 我没有使用 PIT 的经验。它是如何进行突变的? Robolectric 有自己的类加载器并在飞行中进行字节码操作。这就是为什么没有清楚地了解如何将它与 PowerMock 等库一起使用

标签: java android eclipse robolectric pitest


【解决方案1】:

看起来正在发生的是,pit 的代码覆盖存储类的两个副本正在加载。这是一个针对每个测试跟踪每个类的线路覆盖率的类。

被测类由加载时分配给它们的整数 id 标识 - 该 id 嵌入到由调用代码覆盖存储类的字节码操作添加的探测调用中。

代码假定每个类 id 在存储中都有一个可用条目,因为每个 id 在加载时都在存储中注册。这一假设被打破,因为接收探测调用的类的版本与最初注册类的版本不同。

这就是说 0.31 及以下的坑看起来与 Roboelectric 不兼容。

我需要查看一下 Roboelectric 在幕后所做的确切工作,看看是否可以在未来的版本中解决此问题。

----更新---

0.32-SNAPSHOT 版本似乎适用于 Roboelectric(请参阅 cmets)。

【讨论】:

  • 我知道让 PIT 与 Robolectric 兼容可能是一件困难的事情。然而,PIT 和 Robolectric 都会从中受益良多。很高兴看到两者一起工作:)
  • 现在跟踪为 code.google.com/p/pitestrunner/issues/detail?id=84 。看起来好像可以在 RoboElectric 方面进行非常简单的修复。我更愿意在坑代码中寻找更通用的修复 - 也许以某种方式将覆盖存储的功能移动到 java.lang 包中,这样它就不会受到所有非委托类加载器问题的影响。
  • @sulai 在最新的 0.32-SNAPSHOT 中删除了覆盖存储的阻止程序。针对github.com/robolectric/RobolectricSample 尝试它运行没有错误。不过,突变分数看起来非常低,因此可能还有更多问题。
  • 快照是否已经可用?我试过maven-repository.com/artifact/org.pitest。我来错地方了吗?
  • 快照并没有达到 maven central - 它可以从 sonatype repos oss.sonatype.org/content/repositories/snapshots/org/pitest
猜你喜欢
  • 1970-01-01
  • 2018-04-08
  • 2015-07-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多