跳到主要内容

Android 检测工具化测试

在自定义 Application 类中覆盖生产模块

单元测试不同,在单元测试中,你可以在每个测试类中有效地调用 startKoin(即 startKoinKoinTestExtension),在 Instrumented 测试中,Koin 由你的 Application 类启动。

为了覆盖生产 Koin 模块,loadModulesunloadModules 通常是不安全的,因为更改不会立即应用。相反,推荐的方法是将覆盖的 module 添加到 Application 类中 startKoin 使用的 modules 中。 如果你想保持应用程序继承 Application 的类不变,你可以在 AndroidTest 包中创建另一个类,如下所示:

class TestApplication : Application() {
override fun onCreate() {
super.onCreate()
startKoin {
modules(productionModule, instrumentedTestModule)
}
}
}

为了在你的 Instrumentation 测试中使用这个自定义 Application,你可能需要创建一个自定义的 AndroidJUnitRunner,如下所示:

class InstrumentationTestRunner : AndroidJUnitRunner() {
override fun newApplication(
classLoader: ClassLoader?,
className: String?,
context: Context?
): Application {
return super.newApplication(classLoader, TestApplication::class.java.name, context)
}
}

然后用以下代码将其注册到你的 gradle 文件中:

testInstrumentationRunner "com.example.myapplication.InstrumentationTestRunner"

使用测试规则覆盖生产模块

如果你想要更大的灵活性,你仍然需要创建自定义的 AndroidJUnitRunner,但是你可以将 startKoin { ... } 放在自定义测试规则中,而不是放在自定义应用程序中,如下所示:

class KoinTestRule(
private val modules: List<Module>
) : TestWatcher() {
override fun starting(description: Description) {

if (getKoinApplicationOrNull() == null) {
startKoin {
androidContext(InstrumentationRegistry.getInstrumentation().targetContext.applicationContext)
modules(modules)
}
} else {
loadKoinModules(modules)
}
}

override fun finished(description: Description) {
unloadKoinModules(modules)
}
}

这样,我们就可以直接从我们的测试类中覆盖定义,如下所示:

private val instrumentedTestModule = module {
factory<Something> { FakeSomething() }
}

@get:Rule
val koinTestRule = KoinTestRule(
modules = listOf(productionModule, instrumentedTestModule)
)