Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Realm unit test with Robolectric and PowerMock generating zero % code coverage

I followed the instructions here:

  • https://github.com/robolectric/robolectric/wiki/Using-PowerMock
  • https://github.com/realm/realm-java/tree/master/examples/unitTestExample

to set up my realm unit tests. The tests all pass but when I try to generate the code coverage to push to SonarQube, the coverage report shows 0% coverage. When I execute the standard Android Studio code coverage (as described in the answer here: How to get code coverage using Android Studio?) it generates a report which shows 91% coverage. The problem with this approach however, is that it's an html report and there's no way to let it generate an xml report which can be used for SonarQube.

This is the code of the test class

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

I think that the problem is the following line:

@Rule
public PowerMockRule rule = new PowerMockRule();

However, if I remove this line I get the following error although The @PrepareForTest line is unchanged.:

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.
like image 614
Len Goyvaerts Avatar asked Oct 30 '22 18:10

Len Goyvaerts


1 Answers

I guess you are using Jacoco to collect coverage. And PowerMock has a known issue with Jacoco/EclEmma. We're going to fix it in next release.

Update: PowerMock 1.6.6 has been released. It includes fix for JaCoCo offline instrumented code. So, now you may get code coverage with using JaCoCo maven plugin and offline instrumentation

like image 114
Arthur Zagretdinov Avatar answered Nov 15 '22 12:11

Arthur Zagretdinov