Android 虽然域是模拟的,但调用了实际的close()方法

Android 虽然域是模拟的,但调用了实际的close()方法,android,unit-testing,kotlin,realm,mockito,Android,Unit Testing,Kotlin,Realm,Mockito,我有一个非常简单的save()方法来保存realmodel,在保存realm之后。由于不再需要数据库,因此调用close() 问题:调用实际的close()方法,尽管使用mockito模拟Realm。这会导致一个异常: IllegalStateException:来自错误线程的域访问。 领域实例只能在创建它的线程上关闭 莫基托不能模拟王国吗?我不想仅仅为了测试这个案例而包含PowerMock:D 使用realm gradle插件测试 被试班级 import io.realm.Realm imp

我有一个非常简单的
save()
方法来保存
realmodel
,在保存
realm之后。由于不再需要数据库,因此调用close()

问题:调用实际的
close()
方法,尽管使用mockito模拟Realm。这会导致一个异常:

IllegalStateException:来自错误线程的域访问。 领域实例只能在创建它的线程上关闭

莫基托不能模拟王国吗?我不想仅仅为了测试这个案例而包含PowerMock:D

使用
realm gradle插件测试


被试班级

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

单元测试

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

在2.x时代,领域中曾经有很多最终的类和方法,所以您无法模拟它

我做了一个简单的包装器类来解决它,在代码中使用它并模拟它

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)
    }
}
开放类RealmInstance(val realm:realm):可按领域关闭{
打开乐趣删除(clazz:Class){
realm.delete(clazz)
}
OpenFunInsertorUpdate(realmModel:realmModel){
realm.insertOrUpdate(realmModel)
}
打开趣味插件更新(对象:收藏?){
realm.insertOrUpdate(对象)
}
打开有趣的地方(clazz:Class):RealmQueryInstance{
返回RealmQueryInstance(realm.where(clazz))
}
打开有趣的copyFromRealm(realmObject:E):E{
返回realm.copyFromRealm(realmObject)
}
开放式有趣的开始(){
realm.beginTransaction()
}
open fun commitTransaction(){
realm.commitTransaction()
}
打开有趣的copyToRealmOrUpdate(realmObject:T):T{
返回realm.copyToRealmOrUpdate(realmObject)
}
打开有趣的copyToRealmOrUpdate(realmList:realmList):列表{
返回realm.copyToRealmOrUpdate(realmList)
}
}
但现在就我所见,realm 5的
realm
类不再是最终类,你应该可以用Mockito来模拟它:


解决方案是一个新的领域版本

似乎在
realm gradle plugin
5.0.0和5.3.1之间的某个地方,这个问题在realm库端得到了解决,尽管我找不到任何关于它的文档

这就是我最后不得不完全测试的
TestedClass


通过单元测试

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()
}
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)
    }
}
import com.nhaarman.mockito_kotlin.argumentCaptor
导入com.nhaarman.mockito_kotlin.inoorder
导入com.nhaarman.mockito_kotlin.mock
导入com.nhaarman.mockito_kotlin.verifynomore
@试验
趣味储蓄{
val testedClass=testedClass()
val mockRealm:Realm=mock()
val objecttobesave:realmodel=mock()
val captor=argumentCaptor()
testedClass.save(mockRealm,objecttobesave)
inoorder(mockRealm).apply{
验证(mockRealm).executeTransaction(captor.capture())
验证(mockRealm.close())
//现在,请确保传递给executeTransaction()的lambda执行正确的操作
captor.firstValue.execute(mockRealm)
验证(mockRealm).copyToRealm(objectToBeSaved)
verifyNoMoreInteractions(mockRealm)
}
}

你尝试过类似于
Mockito.when(mockRealm.close()).doNothing()之类的东西吗?是的,我尝试过
doNothing()。where(mockRealm.close()
你使用的是哪个领域版本?我使用的是5.0.0版本,我更新了我的问题以反映这一点,并且我给出了一个适用于新版本的答案。