如何模拟Android Lifecycle组件?

时间:2018-12-24 15:12:20

标签: android unit-testing kotlin android-livedata

我正在尝试按照recommended design structure来构建Android应用。

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

根据我的理解,最干净的方法是分别使用UserRepositorySettingsRepository。当然,设置应该具有某种模型,我们将其称为SettingsModel,以便能够以Map的形式检索设置。请注意,这不是ViewModel,它与用户界面无关。

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

SettingsModel需要将“原始数据库数据”转换为地图,以便我可以进行如下设置:settings.show_profile_picturessettings.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)

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

0 个答案:

没有答案
相关问题