Как ввести класс, который использует androidContext в инструментальном тесте с Koin? - PullRequest
0 голосов
/ 07 февраля 2019

У одного из моих классов есть зависимость типа Context.Прежде чем добавить Koin в свой проект, я инициализировал это жесткой зависимостью от моего класса Application:

class ProfileRepository(
    private var _context: Context? = null,
    private var _profileRestService: IProfileRestService? = null
) : IProfileRepository {

    init {
        if (_context == null) {
            _context = MyApplication.getInstance().applicationContext
        }
    }

Теперь я хочу использовать Koin для внедрения этой зависимости.Вот как я определил модуль:

object AppModule {

    @JvmField
    val appModule = module {
        single<IProfileRestService> { ProfileRestService() }
        single<IProfileRepository> { ProfileRepository(androidContext(), get()) }
    }
}

Я запускаю Koin в методе onCreate моего класса Application (который написан на Java):

startKoin(singletonList(AppModule.appModule));

Я хочу протестировать этот класс с помощью инструментального теста, а не модульного теста, потому что я хочу использовать реальный контекст, а не макет.Это мой тест:

@RunWith(AndroidJUnit4::class)
class MyTest : KoinTest {

    private val _profileRepository by inject<IProfileRepository>()

    @Test
    fun testSomething() {
        assertNotNull(_profileRepository)
    }

Тест не пройден с исключением:

org.koin.error.BeanInstanceCreationException: Can't create definition for 'Single [name='IProfileRepository',class='com.my.app.data.profile.IProfileRepository']' due to error :
No compatible definition found. Check your module definition

Я могу заставить его работать с модульным тестом, если я поиграю в контексте следующим образом:

class MyTest : KoinTest {

    private val _profileRepository by inject<IProfileRepository>()

    @Before
    fun before() {
        startKoin(listOf(AppModule.appModule)) with mock(Context::class.java)
    }

    @After
    fun after() {
        stopKoin()
    }

    @Test
    fun testSomething() {
        assertNotNull(_profileRepository)
    }

Как я могу заставить его работать в качестве инструментального теста в реальном контексте?

Ответы [ 4 ]

0 голосов
/ 20 марта 2019

Очевидно, что нет способа запустить Koin из класса Java и внедрить контекст приложения.Это означает, что если один из ваших классов должен получить контекст из контейнера, вы должны использовать org.koin.android.ext.android.startKoin вместо org.koin.java.standalone.KoinJavaStarter.startKoin.

Поскольку мой класс Application все еще написан на Java, я создал объектвызвал KoinHelper с одним методом:

@JvmStatic
fun start(application: Application) {
    application.startKoin(application, listOf(AppModule.appModule))
}

Затем я вызвал это из onCreate метода моего класса Application:

KoinHelper.start(this);

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

Пожалуйста, смотрите эту проблему на GitHub для получения дополнительной информации.

0 голосов
/ 20 февраля 2019

Пожалуйста, отметьте этот раздел в документации.Он говорит

, если вам нужно запустить Koin из другого класса Android, вы можете использовать функцию startKoin () и предоставить вашему экземпляру контекста Android такой же код:

startKoin (androidContext,myAppModules)

Итак, в своем тесте инструментовки вы можете передать контекст при запуске Koin.

@Before
fun before() {
        startKoin(InstrumentationRegistry.getContext(), listOf(AppModule.appModule))
    }

Или, если вам нужен контекст уровня приложения

@Before
fun before() {
        startKoin(InstrumentationRegistry.getTargetContext(), listOf(AppModule.appModule))
    }

Ссылочная документация предназначена для версии 1.0.1

0 голосов
/ 09 марта 2019

С точки зрения получения контекста Application в инструментальном тесте, вы можете использовать androidx.test.core.app.ApplicationProvider или InstrumentationRegistry.targetContext.applicationContext.

  @Before
  fun setUp() {
    stopKoin()
    loadKoinModules(testModule) with ApplicationProvider.getApplicationContext<Application>()
  }

..., где testModule использует androidApplication() для извлеченияконтекст Application:

val testModule = module {
  single {
    ToDoDatabase.newInstance(
      androidApplication(),
      memoryOnly = true
    )
  }
  single { ToDoRepository(get()) }
}

Обратите внимание, что мой вызов stopKoin() происходит потому, что у меня были трудности с переопределением существующего модуля, созданного startKoin() в моем пользовательском подклассе Application.¯\_(ツ)_/¯

0 голосов
/ 12 февраля 2019

Вместо (в приложении):

startKoin(applicationContext, modules)

Использовать смоделированный контекст:

startKoin(modules) with (mock(Context::class.java))

С https://insert -koin.io / docs / 1.0 /документация / koin-андроид / index.html # _starting_koin_with_android_context_from_elsewhere

...