Hesam Hesam - 2 months ago 9
Android Question

Realm throws NullPointerException when I am inserting into the db

I have a RealmManager class and I have several methods in it in order to perform CRUD. this is my

save
method in this class.

public void save(final List<PointOfInterest> pointOfInterestList) {
if (mRealm == null || pointOfInterestList == null || pointOfInterestList.isEmpty()) {
return;
}

mRealm.executeTransaction(new Realm.Transaction() { // <= line 155, java.lang.NullPointerException points to this line
@Override
public void execute(Realm realm) {
realm.insertOrUpdate(pointOfInterestList);
}
});
}


I wrote a junit test in order to test it.

@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 19)
@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*"})
@SuppressStaticInitializationFor("io.realm.internal.Util")
@PrepareForTest({Realm.class, RealmLog.class})
public class RealmPOIManagerTest
{

@Rule
public PowerMockRule rule = new PowerMockRule();
private Realm mockRealm;

@Before
public void setUp() throws Exception
{
mockStatic(RealmLog.class);
mockStatic(Realm.class);

Realm mockRealm = PowerMockito.mock(Realm.class);
when(Realm.getDefaultInstance()).thenReturn(mockRealm);

this.mockRealm = mockRealm;
}

@Test
public void shouldVerifyThatPOIWasCreated() {
List<PointOfInterest> poiList = new ArrayList<>(1);

doCallRealMethod().when(mockRealm).executeTransaction(Mockito.any(Realm.Transaction.class));

PointOfInterest pointOfInterest = mock(PointOfInterest.class);
when(mockRealm.createObject(PointOfInterest.class)).thenReturn(pointOfInterest);

poiList.add(pointOfInterest);
RealmPOIManager.getInstance().save(poiList);

// Verify that Realm#createObject was called only once and
// Verify that a PointOfInterest was in fact created.
verify(mockRealm, times(1)).insertOrUpdate(poiList);
}

@Test
public void shouldVerifyCRUD()
{
List<PointOfInterest> poiList = getCollectionOfPOIs();

// Create list elements
RealmPOIManager.getInstance().save(poiList); // <= line 112

// Read a POI item from db based on id
PointOfInterest result = RealmPOIManager.getInstance().loadPOIByBookingId("1");

// Make sure retrieved POI is as same as the original item
assertThat(poiList.get(0), is(result));

// Update POI, save it and make sure the item has been updated
result.setUid("4");
List<PointOfInterest> list = new ArrayList<>(1);
list.add(result);
RealmPOIManager.getInstance().save(list);
PointOfInterest result2 = RealmPOIManager.getInstance().loadPOIByBookingId("4");
assertThat(result, is(result2));
PointOfInterest result3 = RealmPOIManager.getInstance().loadPOIByBookingId("1");
assertNull(result3);
}

private List<PointOfInterest> getCollectionOfPOIs()
{
List<PointOfInterest> poiList = new ArrayList<>(3);

PointOfInterest poi1 = new PointOfInterest();
poi1.setUid("1");

PointOfInterest poi2 = new PointOfInterest();
poi2.setUid("2");

PointOfInterest poi3 = new PointOfInterest();
poi3.setUid("3");

poiList.add(poi1);
poiList.add(poi2);
poiList.add(poi3);

return poiList;
}
}


The first test
shouldVerifyThatPOIWasCreated
passes successfully while the second one fails. This is what is see in log:

java.lang.NullPointerException
at io.realm.BaseRealm.beginTransaction(BaseRealm.java:337)
at io.realm.Realm.beginTransaction(Realm.java:122)
at io.realm.Realm.executeTransaction(Realm.java:1087)
at com.my.package.db.dao.RealmPOIManager.save(RealmPOIManager.java:155)
at com.my.package.sdk.db.dao.RealmPOIManagerTest.shouldVerifyCRUD(RealmPOIManagerTest.java:112)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.powermock.modules.junit4.rule.PowerMockStatement$1.run(PowerMockRule.java:65)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.powermock.reflect.internal.WhiteboxImpl.performMethodInvocation(WhiteboxImpl.java:1873)
at org.powermock.reflect.internal.WhiteboxImpl.doInvokeMethod(WhiteboxImpl.java:773)
at org.powermock.reflect.internal.WhiteboxImpl.invokeMethod(WhiteboxImpl.java:638)
at org.powermock.reflect.Whitebox.invokeMethod(Whitebox.java:401)
at org.powermock.classloading.ClassloaderExecutor.execute(ClassloaderExecutor.java:98)
at org.powermock.classloading.ClassloaderExecutor.execute(ClassloaderExecutor.java:78)
at org.powermock.modules.junit4.rule.PowerMockStatement.evaluate(PowerMockRule.java:57)
at org.robolectric.RobolectricTestRunner$2.evaluate(RobolectricTestRunner.java:251)
at org.robolectric.RobolectricTestRunner.runChild(RobolectricTestRunner.java:188)
at org.robolectric.RobolectricTestRunner.runChild(RobolectricTestRunner.java:54)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:152)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:119)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)


Process finished with exit code 255


Any idea would be appreciated. Thanks.

Update



This is part of my PointOfInterest class.

@Parcel
@RealmClass
public class PointOfInterest implements RealmModel {
public static final String REALM_KEY_UID = "uid";

@Required // Throw exception if BookingId is `null`
@PrimaryKey
String uid;

...
}

Answer

Realm doesn't work in Unit Tests. The unit test example on Realm's github shows that Realm is mocked, because Realm doesn't work outside Android.

To test Realm-specific behavior, you'd need to use an instrumentation test.

(This essentially means that

 doCallRealMethod().when(mockRealm).executeTransaction(Mockito.any(Realm.Transaction.class));

This won't work, the real method called in a non-Android JVM context will fail.)

Comments