Android:如何使用Robolectric运行PIT变异测试?

4
如何使用RobolectricPIT测试Android应用程序?
使用Robolectric,您可以在JVM中运行Android测试。使用PIT,您可以显示代码行覆盖率并进行变异测试。对我来说,使用Eclipse +插件也可以,但不是必需的。
这是我迄今为止尝试过的内容:
我有一个Android项目,让我们称其为“我的项目”(MyProject)。
现在,我想使用RobolectricPIT在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项目依赖项。

因此,我的第二次尝试是使用命令行,如PIT快速入门所述:

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

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

"

<classpath> 包含:junit4、robolectric、MyProject 类文件、MyTest 类文件、android.jar 和其他必要的 Android 库。

一旦这个 JUnit 测试成功,我就在 MyProject 的根路径下使用相同的 <classpath> 进行 PIT 调用,并执行该调用:

"
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 - 但从快速搜索来看,只要正常的JVM字节码被写入磁盘的某个地方,它应该可以与PIT一起使用。如果您成功(或失败),请发布到PIT Google用户组。 - henry
@henry 在第二次尝试中,我尝试使用命令行中的PIT。普通的JUnit测试(不使用robolectric)与PIT一起工作得很好。然而,使用robolectric的MyActivityTest会产生上面发布的异常。您有任何关于问题可能是什么的想法吗? - sulai
我没有使用过PIT。它如何进行变异?Robolectric有自己的类加载器,并在运行时进行字节码操作。这就是为什么没有清楚的理解如何将其与PowerMock等库一起使用的原因。 - Eugen Martynov
1个回答

4
看起来发生的情况是 pit 的代码覆盖存储类的两个副本被加载了。这是一个跟踪每个类对每个测试的行覆盖率的类。
要测试的类通过分配给它们的整数 id 进行标识,这个 id 在加载时被嵌入到字节码操作中添加的探针调用中,该调用会调用代码覆盖存储类。
代码假定每个类 id 都有一个可用的存储条目,在加载时每个 id 都在存储器中注册。但此假设被打破,因为接收探针调用的类的版本与最初注册类的版本不同。
长话短说,pit 0.31 及以下版本似乎与 Roboelectric 不兼容。
我需要仔细查看 Roboelectric 的后台工作,以确定这是否可以在未来的发布中修复。
---- 更新 ----
0.32-SNAPSHOT 版本似乎能够与 Roboelectric 一起使用(请参见注释)。

我知道使PIT与Robolectric兼容可能是一件困难的事情。然而,PIT和Robolectric都会从中受益良多。希望看到它们一起工作 :) - sulai
现在已经将其跟踪为http://code.google.com/p/pitestrunner/issues/detail?id=84。看起来在RoboElectric方面可能有一个非常简单的修复方法。我更喜欢在pit代码中寻找更通用的解决方案-也许是将覆盖存储的功能移动到java.lang包中,这样它就不会受到所有非委托类加载器问题的影响。 - henry
1
@sulai 最新的0.32-SNAPSHOT版本已移除覆盖率存储器中的阻塞程序。针对 https://github.com/robolectric/RobolectricSample 进行尝试,未出现错误。但突变分数看起来异常低,可能存在进一步的问题。 - henry
快照已经可用了吗?我尝试了http://maven-repository.com/artifact/org.pitest。我在错误的地方吗? - sulai
1
快照版本无法到达Maven中央仓库 - 它可以从Sonatype仓库获取 https://oss.sonatype.org/content/repositories/snapshots/org/pitest/ - henry
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接