【问题标题】:Realm unit test with Robolectric and PowerMock generating zero % code coverage使用 Robolectric 和 PowerMock 进行领域单元测试,生成零 % 代码覆盖率
【发布时间】:2017-03-02 14:36:44
【问题描述】:

我按照这里的说明进行操作:

设置我的领域单元测试。测试全部通过,但是当我尝试生成代码覆盖率以推送到 SonarQube 时,覆盖率报告显示 0% 的覆盖率。当我执行标准的 Android Studio 代码覆盖率时(如此处的答案所述:How to get code coverage using Android Studio?),它会生成一个显示 91% 覆盖率的报告。然而,这种方法的问题在于它是一个 html 报告,无法让它生成可用于 SonarQube 的 xml 报告。

这是测试类的代码

@RunWith(RobolectricTestRunner.class)
@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*"})
@PrepareForTest({Realm.class, RealmConfiguration.class, RealmCore.class, RealmLog.class})
@SuppressStaticInitializationFor("io.realm.internal.Util")
@Config(constants = BuildConfig.class, manifest =    "src/main/AndroidManifest.xml", sdk = 21)
public class DecisionTreeTest
{
    @Captor
    ArgumentCaptor<Realm.Transaction.Callback> realmCallbackCaptor;

    // Robolectric, Using Power Mock https://github.com/robolectric/robolectric/wiki/Using-PowerMock
    @Rule
    public PowerMockRule rule = new PowerMockRule();

    private Context mockContext;

    private final byte[] fakeRealmKey = {
        -122, -115, -113, -111, -105, -104, -101, -99, -94, -93, -90, -87,
        -77, -74, -67, -66, -63, -61, -56, -53, -48, -47, -33, -31,
        -30, -28, -22, -17, -5, -3, -1, 3, 8, 11, 17, 18,
        21, 22, 27, 30, 40, 42, 51, 52, 53, 54, 57, 59,
        61, 63, 67, 70, 74, 76, 78, 85, 90, 91, 103, 108,
        113, 117, 119, 127
    };

    @Before
    public void setUp() throws Exception
    {
        // Setup Realm to be mocked. The order of these matters
        mockStatic(RealmCore.class);
        mockStatic(RealmLog.class);
        mockStatic(Realm.class);
        mockStatic(RealmConfiguration.class);

        this.mockContext = RuntimeEnvironment.application;

        Whitebox.setInternalState(
                Realm.class,
                "applicationContext",
                RuntimeEnvironment.application);

        /*
        Better solution would be just mock the RealmConfiguration.Builder class.
        But it seems there is some problems for powermock to mock it (static inner class).
        We just mock the RealmCore.loadLibrary(Context) which will be called by
        RealmConfiguration.Builder's constructor.
        */
        doNothing().when(RealmCore.class);
        RealmCore.loadLibrary(any(Context.class));
    }

    @Test(expected = DecisionTreeException.class)
    public void persistSurvey_DecisionTreeRealmNotEnabled_ThrowsException() throws Exception
    {
        DecisionTree decisionTree = createSimpleDecisionTree();
        Survey survey = decisionTree.getSurveyFromResource(R.raw.survey);
        decisionTree.persistSurvey(survey, null, null);
    }

    @Test(expected = DecisionTreeException.class)
    public void persistSurvey_NullAsFirstParam_ThrowsException() throws Exception
    {
        DecisionTree decisionTree = createRealmDecisionTree();
        decisionTree.persistSurvey(null, null, null);
    }

    @Test
    public void persistSurvey_SurveyAsFirstParam_ThrowsException() throws Exception
    {
        final Realm mockRealm = mock(Realm.class);
         when(Realm.getInstance(any(RealmConfiguration.class))).thenReturn(mockRealm);

        org.mockito.stubbing.Answer<Void> executeAnswer = new org.mockito.stubbing.Answer<Void>()
        {
            @Override
            public Void answer(InvocationOnMock invocation) throws Throwable
            {
                ((Realm.Transaction) invocation.getArguments()[0]).execute(mockRealm);
                return null;
            }
        };

        doAnswer(executeAnswer)
            .when(mockRealm)
            .executeTransactionAsync(
                    any(Realm.Transaction.class),
                    any(Realm.Transaction.OnSuccess.class),
                    any(Realm.Transaction.OnError.class));

        DecisionTree decisionTree = createRealmDecisionTree();
        Survey survey = decisionTree.getSurveyFromResource(R.raw.survey);

        decisionTree.persistSurvey(survey, null, null);

        verify(mockRealm).executeTransactionAsync(
            any(Realm.Transaction.class),
            any(Realm.Transaction.OnSuccess.class),
            any(Realm.Transaction.OnError.class));
        verify(mockRealm).copyToRealmOrUpdate(any(Survey.class));
    }

    private DecisionTree createRealmDecisionTree()
    {
        return new DecisionTree.Builder()
            .setContext(mockContext)
            .setRealmKey(fakeRealmKey)
            .setRealmEnabled(true)
            .build();
    }

    private DecisionTree createSimpleDecisionTree()
    {
        return new DecisionTree.Builder()
            .setContext(RuntimeEnvironment.application)
            .build();
    }
}

我认为问题出在以下行:

@Rule
public PowerMockRule rule = new PowerMockRule();

但是,如果我删除此行,尽管 @PrepareForTest 行未更改,但我会收到以下错误。:

org.powermock.api.mockito.ClassNotPreparedException: 
The class io.realm.internal.RealmCore not prepared for test.
To prepare this class, add class to the '@PrepareForTest' annotation.
In case if you don't use this annotation, add the annotation on class or  method level.

【问题讨论】:

  • 我完全放弃 Powermock 的原因之一就是:有时,有些东西根本不起作用。你花了 小时 研究它;大多数情况下没有找到任何解决方案。
  • 你能分享你的课程资源吗?

标签: unit-testing realm code-coverage robolectric powermock


【解决方案1】:

我猜你正在使用 Jacoco 来收集报道。 PowerMock 与 Jacoco/EclEmma 有一个已知的issue。我们将在下一个版本中修复它。

更新:PowerMock 1.6.6 已经发布。它包括对 JaCoCo 离线检测代码的修复。所以,现在你可以使用 JaCoCo maven 插件和offline instrumentation 获得代码覆盖率

【讨论】:

  • 我试过 Jacoco 和 Cobertura,两者都给 0%。知道下一个版本什么时候发布吗?
  • 希望,我们会在 11 月中旬发布一个新版本。
  • @LenGoyvaerts 新版本已发布。它包括对 JaCoCo 离线检测代码的修复。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-10-07
  • 2016-05-04
  • 2014-09-26
  • 1970-01-01
  • 1970-01-01
  • 2012-01-18
相关资源
最近更新 更多