Как издеваться над компонентами Android Lifecycle? - PullRequest
0 голосов
/ 24 декабря 2018

Я пытаюсь создать приложение для Android в соответствии с рекомендуемой структурой проектирования .

Допустим, есть UserRepository для работы с пользователями.Тем не менее, я хотел бы иметь определенные настройки в приложении, например, «Показать изображение профиля», «Сортировать по» и т. Д. Я хотел бы сохранить эти настройки в базе данных комнат, как пользователи.

Насколько я понимаю, самый чистый способ - иметь отдельные UserRepository и SettingsRepository.И, конечно, в настройках должна быть своего рода модель, назовем ее SettingsModel, чтобы можно было получить настройки, например, Map.Обратите внимание, что это не ViewModel, он не имеет ничего общего с пользовательским интерфейсом.

Затем UserRepository должен реализовать свой собственный бизнес (управление пользователями), как в примере, связанном выше.,Кроме того, он также должен иметь зависимость SettingsModel, чтобы он мог легко извлекать настройки, которые влияют на способ извлечения пользователей.

SettingsModel необходимо включить «необработанную базу данных»данные "в карту, чтобы я мог получить настройки, как это: settings.show_profile_pictures и settings.sort_by, и т. д. Чтобы добиться этого, мне нужно извлечь данные из LiveData, что подразумевает, что мне нужно наблюдатьчто LiveData, чтобы я мог обновлять Map всякий раз, когда меняются настройки.

И тут возникает проблема: для метода observe() требуется LifecycleOwner, который я не могу предоставить в своих тестах,

1-я попытка: макет с Mockito

Это был бы инструментальный тест, потому что таким образом у меня был доступ к Activity, который необходим для получения DAO.

Я пытаюсь @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)

2-я попытка: использовать Roboelectric для создания AppCompatActivity, который на самом деле является LifecycleOwner.

Это не инструментальный тест, потому что Roboelectric не работает в среде androidTest.

Activity все еще нужно высмеивать, однако он выбрасывает 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)
    }
}

Исключение:

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)

Действительно ли невозможно протестировать что-либо, включающее компоненты жизненного цикла?

...