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