1

I have a fairly simple save() method to persist a RealmModel, after saving realm.close() is called because the database is no longer needed.

The problem: The actual close() method is called although Realm is mocked using mockito. This causes an exception:

IllegalStateException: Realm access from incorrect thread. Realm instance can only be closed on the thread it was created.

Is mockito not able to mock Realm? I don't want to include PowerMock just for testing this case :D

Tested using realm-gradle-plugin 5.0.0


The tested class

import io.realm.Realm
import io.realm.RealmModel

class TestedClass {

    fun save(realm: Realm, objectToBeSaved: RealmModel) {
        // Persist your data in a transaction
        realm.executeTransaction {
            // Using executeTransaction with a lambda reduces code size
            // and makes it impossible to forget to commit the transaction.
            it.copyToRealm(objectToBeSaved)
        }
        // Close database after saving (this causes the exception)
        realm.close()
    }
}

The Unit Test

import com.nhaarman.mockito_kotlin.any
import com.nhaarman.mockito_kotlin.mock
import com.nhaarman.mockito_kotlin.verify

@Test
fun save() {
    val testedClass = TestedClass()
    val mockRealm: Realm = mock()
    val objectToBeSaved: RealmModel = mock()

    testedClass.save(mockRealm, objectToBeSaved) // this causes the exception

    verify(mockRealm).executeTransaction(any())
    verify(mockRealm).copyToRealm(objectToBeSaved)
    verify(mockRealm).close()
}
Sebastian
  • 2,896
  • 23
  • 36

2 Answers2

2

A new Realm version was the solution.

It seems that somewhere between realm-gradle-plugin 5.0.0 and 5.3.1 this issue got fixed on the Realm library side, even though I couldn't find any documentation on the changelog that mentions it.

This is what I ended up having to fully test TestedClass


The Passing Unit Test

import com.nhaarman.mockito_kotlin.argumentCaptor
import com.nhaarman.mockito_kotlin.inOrder
import com.nhaarman.mockito_kotlin.mock
import com.nhaarman.mockito_kotlin.verifyNoMoreInteractions

@Test
fun save() {
    val testedClass = TestedClass()
    val mockRealm: Realm = mock()
    val objectToBeSaved: RealmModel = mock()
    val captor = argumentCaptor<Realm.Transaction>()

    testedClass.save(mockRealm, objectToBeSaved)

    inOrder(mockRealm).apply {
        verify(mockRealm).executeTransaction(captor.capture())
        verify(mockRealm).close()

        // Now make sure the lambda passed to executeTransaction() does the right thing
        captor.firstValue.execute(mockRealm)

        verify(mockRealm).copyToRealm(objectToBeSaved)
        verifyNoMoreInteractions(mockRealm)
    }
}
Sebastian
  • 2,896
  • 23
  • 36
0

There used to be a lot of final classes and methods in Realm in the 2.x days, so you were not able to mock it.

I did a simple wrapper class to work around it, used it in my code and mocked it

open class RealmInstance(val realm: Realm): Closeable by realm {

    open fun delete(clazz: Class<out RealmModel>) {
        realm.delete(clazz)
    }

    open fun insertOrUpdate(realmModel: RealmModel) {
        realm.insertOrUpdate(realmModel)
    }

    open fun insertOrUpdate(objects: Collection<RealmModel>?) {
        realm.insertOrUpdate(objects)
    }

    open fun <E : RealmModel> where(clazz: Class<E>): RealmQueryInstance<E> {
        return RealmQueryInstance(realm.where(clazz))
    }

    open fun <E: RealmModel> copyFromRealm(realmObject: E): E {
        return realm.copyFromRealm(realmObject)
    }

    open fun beginTransaction() {
        realm.beginTransaction()
    }

    open fun commitTransaction() {
        realm.commitTransaction()
    }

    open fun <T: RealmObject> copyToRealmOrUpdate(realmObject: T): T {
        return realm.copyToRealmOrUpdate(realmObject)
    }

    open fun <T: RealmObject> copyToRealmOrUpdate(realmList: RealmList<T>): List<T> {
        return realm.copyToRealmOrUpdate(realmList)
    }
}

But nowadays as far as I can see with realm 5 the Realm class is not final anymore and you should be able to mock it with Mockito:

https://realm.io/docs/java/5.0.0/api/

TpoM6oH
  • 8,385
  • 3
  • 40
  • 72