I followed the instructions here:
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With