【问题标题】:How do I inject a class that uses androidContext in an instrumented test with Koin?如何在 Koin 的检测测试中注入使用 androidContext 的类?
【发布时间】:2019-02-07 19:31:09
【问题描述】:

我的一个类具有 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()) }
    }
}

我在我的 Application 类(用 Java 编写)的 onCreate 方法中启动 Koin:

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)
    }

我怎样才能使它在真实上下文中作为仪器测试工作?

【问题讨论】:

标签: android junit kotlin dependency-injection koin


【解决方案1】:

代替(在应用程序中):

startKoin(applicationContext, modules)

使用模拟的上下文:

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

来自https://insert-koin.io/docs/1.0/documentation/koin-android/index.html#_starting_koin_with_android_context_from_elsewhere

【讨论】:

  • 我专门尝试使用真实上下文进行仪器测试。
【解决方案2】:

显然没有办法从 Java 类启动 Koin 并注入应用程序上下文。这意味着如果您的某个类需要从容器中获取上下文,您必须使用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))
}

然后我从我的 Application 类的 onCreate 方法中调用它:

KoinHelper.start(this);

现在,我在原始答案中发布的仪器测试运行良好。

请参阅this issue on GitHub 了解更多信息。

【讨论】:

    【解决方案3】:

    请查看文档中的this 部分。它说

    如果您需要从另一个 Android 类启动 Koin,您可以使用 startKoin() 函数并为您的 Android Context 实例提供 就像:

    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

    【讨论】:

    • 在仪器测试中,我无法调用startKoin,因为它已经在应用程序类中启动。我认为我遇到的问题实际上是 koin-java 的一个错误。见github.com/InsertKoinIO/koin/issues/352
    【解决方案4】:

    就在插桩测试中获取Application 上下文而言,您可以使用androidx.test.core.app.ApplicationProviderInstrumentationRegistry.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 子类中创建的现有模块时遇到了困难。 ¯\_(ツ)_/¯

    【讨论】:

    • 这可能行得通,但我选择了另一个解决方案,我会发布。
    【解决方案5】:
    @Before
    fun setUp() {
        stopKoin()
        startKoin {
            androidContext(app) // for example ApplicationProvider.getApplicationContext<TestApplication>()
            modules(module1, module2)
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-10-03
      • 1970-01-01
      • 2022-12-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-13
      相关资源
      最近更新 更多