我正在尝试按照recommended design structure来构建Android应用。
比方说,有一个UserRepository
用于处理用户。但是,我想在应用程序中进行某些设置,例如“显示个人资料图片”,“排序依据”等。我想将这些设置存储在Room数据库中,就像用户一样。
根据我的理解,最干净的方法是分别使用UserRepository
和SettingsRepository
。当然,设置应该具有某种模型,我们将其称为SettingsModel
,以便能够以Map
的形式检索设置。请注意,这不是ViewModel
,它与用户界面无关。
然后,UserRepository
应该实现自己的业务(处理用户),就像上面链接的示例一样。除此之外,它还应该具有该SettingsModel
的依赖性,以便它可以轻松地检索设置,从而影响应如何检索用户。
SettingsModel
需要将“原始数据库数据”转换为地图,以便我可以进行如下设置:settings.show_profile_pictures
和settings.sort_by
等。
为此,我需要从LiveData
中提取数据,这意味着我需要观察LiveData
,以便只要更改设置就可以更新Map
。
问题来了:observe()
方法需要一个LifecycleOwner
,我在测试中无法提供。
第一次尝试:使用Mockito模拟
这将是一种仪器化测试,以防我可以访问Activity
,而这是检索DAO所必需的。
我正在尝试使用Dagger @Inject
,但模拟未成功:
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)
第二次尝试:使用Roboelectric创建AppCompatActivity
,实际上是LifecycleOwner
。
这不是仪器化测试,因为Roboelectric在androidTest环境中无法正常工作。
Activity
仍然必须被嘲笑,但是它会抛出NullPointerException
。我能想到的唯一方法是遍历此Exception堆栈,并模拟其中的所有内容(如果可能的话)。但这对我来说听起来很疯狂。必须有一个更好的解决方案。
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)
}
}
例外:
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)
真的不可能测试任何涉及生命周期组件的东西吗?