如何模拟Android生命周期组件?

如何模拟Android生命周期组件?,android,unit-testing,kotlin,android-livedata,Android,Unit Testing,Kotlin,Android Livedata,我正在尝试按照以下步骤构建一个Android应用程序 比如说,有一个UserRepository用于处理用户。但是,我希望在应用程序中有某些设置,例如“显示个人资料图片”、“排序方式”等。我希望像用户一样将这些设置存储在房间数据库中 根据我的理解,最干净的方法是拥有一个单独的用户存储库和一个设置存储库。当然,设置应该有一种模型,我们称之为SettingsModel,以便能够以Map的形式检索设置。请注意,这不是一个视图模型,它与UI无关 然后,UserRepository应该实现自己的业务(处理

我正在尝试按照以下步骤构建一个Android应用程序

比如说,有一个
UserRepository
用于处理用户。但是,我希望在应用程序中有某些设置,例如“显示个人资料图片”、“排序方式”等。我希望像用户一样将这些设置存储在房间数据库中

根据我的理解,最干净的方法是拥有一个单独的
用户存储库
和一个
设置存储库
。当然,设置应该有一种模型,我们称之为
SettingsModel
,以便能够以
Map
的形式检索设置。请注意,这不是一个
视图模型
,它与UI无关

然后,
UserRepository
应该实现自己的业务(处理用户),就像上面链接的示例一样。除此之外,它还应该依赖于
设置模型
,以便可以轻松检索设置,从而影响检索用户的方式

SettingsModel
需要将“原始数据库数据”转换为地图,以便我可以达到如下设置:
settings.show\u profile\u pictures
settings.sort\u by
,等等。 为了实现这一点,我需要从
LiveData
中提取数据,这意味着我需要观察
LiveData
,以便在设置更改时更新
Map

问题来了:
observe()
方法需要一个
LifecycleOwner
,这是我在测试中无法提供的

第一次尝试:用Mockito模拟

这将是一个仪器化的测试,因为这样我就可以访问
活动
,这是检索DAO所需要的

我正试图用匕首
@注入它,但嘲弄没有成功:

class SettingsRepositoryTest {

    private lateinit var settingsDao: SettingsDao

    @Mock
    private lateinit var mockLifecycleOwner: LifecycleOwner

    @Before
    fun createDb(){
        MockitoAnnotations.initMocks(LifecycleOwner::class.java)

        val appContext = InstrumentationRegistry.getTargetContext()

        val db = Room.inMemoryDatabaseBuilder(appContext, CurrencyConverterDb::class.java).allowMainThreadQueries().build()
        settingsDao = db.settingsDao()

    }

    @Test
    fun testSettingsMap() {

        val repo = SettingsRepository(settingsDao, mockLifecycleOwner) // throws the exception here

    }
}
例外情况:

kotlin.UninitializedPropertyAccessException: lateinit property mockLifecycleOwner has not been initialized
at com.helmet91.currencyconverter.repositories.SettingsRepositoryTestInst.testSettingsMap(SettingsRepositoryTestInst.kt:46)
java.lang.NullPointerException
    at org.robolectric.internal.bytecode.ShadowImpl.extract(ShadowImpl.java:17)
    at org.robolectric.shadow.api.Shadow.extract(Shadow.java:25)
    at org.robolectric.Shadows.shadowOf(Shadows.java:1215)
    at org.robolectric.shadows.CoreShadowsAdapter.getMainLooper(CoreShadowsAdapter.java:23)
    at org.robolectric.android.controller.ComponentController.<init>(ComponentController.java:29)
    at org.robolectric.android.controller.ComponentController.<init>(ComponentController.java:21)
    at org.robolectric.android.controller.ActivityController.<init>(ActivityController.java:33)
    at org.robolectric.android.controller.ActivityController.of(ActivityController.java:25)
    at org.robolectric.Robolectric.buildActivity(Robolectric.java:97)
    at org.robolectric.Robolectric.buildActivity(Robolectric.java:93)
    at com.helmet91.currencyconverter.repositories.SettingsRepositoryTest.createDb(SettingsRepositoryTest.kt:29)
第二次尝试:使用Roboelectric创建一个
AppCompatActivity
,它实际上是
生命周期所有者

这不是仪器化测试,因为RoboeElectric不在androidTest环境中工作

活动
仍然需要模拟,但是,它会抛出一个
NullPointerException
。我能想到的唯一方法就是遍历这个异常堆栈,并模拟其中的所有内容,如果可能的话。但我觉得这听起来很疯狂。必须有更好的解决办法

class SettingsRepositoryTest {

    private lateinit var settingsDao: SettingsDao
    private lateinit var activity: AppCompatActivity

    @Before
    fun createDb(){
        val built = Robolectric.buildActivity(MainActivity::class.java) // throws the exception here
        val created = built.create()
        val controller = created.start()
        activity = controller.get() as AppCompatActivity

        val db = Room.inMemoryDatabaseBuilder(activity, CurrencyConverterDb::class.java).allowMainThreadQueries().build()
        settingsDao = db.settingsDao()

    }

    @Test
    fun testSettingsMap() {

        val repo = SettingsRepository(settingsDao, activity)

        val settingsMap = repo.getSettings()

        val settingsEntity = Settings(1, "show_flags", "1", "bool")

        settingsDao.insert(settingsEntity)

        assertTrue(settingsMap.show_flags)
    }
}
例外情况:

kotlin.UninitializedPropertyAccessException: lateinit property mockLifecycleOwner has not been initialized
at com.helmet91.currencyconverter.repositories.SettingsRepositoryTestInst.testSettingsMap(SettingsRepositoryTestInst.kt:46)
java.lang.NullPointerException
    at org.robolectric.internal.bytecode.ShadowImpl.extract(ShadowImpl.java:17)
    at org.robolectric.shadow.api.Shadow.extract(Shadow.java:25)
    at org.robolectric.Shadows.shadowOf(Shadows.java:1215)
    at org.robolectric.shadows.CoreShadowsAdapter.getMainLooper(CoreShadowsAdapter.java:23)
    at org.robolectric.android.controller.ComponentController.<init>(ComponentController.java:29)
    at org.robolectric.android.controller.ComponentController.<init>(ComponentController.java:21)
    at org.robolectric.android.controller.ActivityController.<init>(ActivityController.java:33)
    at org.robolectric.android.controller.ActivityController.of(ActivityController.java:25)
    at org.robolectric.Robolectric.buildActivity(Robolectric.java:97)
    at org.robolectric.Robolectric.buildActivity(Robolectric.java:93)
    at com.helmet91.currencyconverter.repositories.SettingsRepositoryTest.createDb(SettingsRepositoryTest.kt:29)
java.lang.NullPointerException
位于org.roblectric.internal.bytecode.ShadowImpl.extract(ShadowImpl.java:17)
位于org.roblectric.shadow.api.shadow.extract(shadow.java:25)
位于org.roblectric.Shadows.shadowOf(Shadows.java:1215)
位于org.roblectric.shadows.CoreShadowsAdapter.getMainLooper(CoreShadowsAdapter.java:23)
位于org.roblectric.android.controller.ComponentController.(ComponentController.java:29)
位于org.roblectric.android.controller.ComponentController.(ComponentController.java:21)
位于org.roblectric.android.controller.ActivityController。(ActivityController.java:33)
位于org.roblectric.android.controller.ActivityController.of(ActivityController.java:25)
位于org.roblectric.roblectric.buildActivity(roblectric.java:97)
位于org.roblectric.roblectric.buildActivity(roblectric.java:93)
在com.helmet91.currencyconverter.repositories.SettingsRepositoryTest.createDb(SettingsRepositoryTest.kt:29)上

是否真的不可能测试任何涉及生命周期组件的东西?

您是否尝试过类似的方法:?@MatPag噢,谢谢您的提醒。是的,我也试过了,我忘了把它写在帖子里。我会编辑它。SettingsRepository应该公开liveData对象,它不需要生命周期。然后使用不需要生命周期对象的方法
observeForver