如何在 Koin 的仪器测试中注入使用 androidContext 的 class?
How do I inject a class that uses androidContext in an instrumented test with Koin?
我的一个 classes 具有上下文类型的依赖项。在将 Koin 添加到我的项目之前,我通过对我的应用程序 class:
的硬依赖来初始化它
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()) }
}
}
我在我的应用程序 class 的 onCreate
方法中启动 Koin(在 Java 中编写):
startKoin(singletonList(AppModule.appModule));
我想用仪器测试而不是单元测试来测试这个 class,因为我想使用真实的上下文而不是模拟。这是我的测试:
@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)
}
我怎样才能使它作为具有真实上下文的仪器测试工作?
代替(在申请中):
startKoin(applicationContext, modules)
使用模拟上下文:
startKoin(modules) with (mock(Context::class.java))
请检查文档中的 this 部分。它说
if you need to start Koin from another Android class, you can use the
startKoin() function and provide your Android Context instance with
just like:
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
关于在插桩测试中获取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
子类中创建的现有模块。 ¯\_(ツ)_/¯
显然无法从 Java class 启动 Koin 并注入应用程序上下文。这意味着如果您的 classes 之一需要从容器中获取上下文,则必须使用 org.koin.android.ext.android.startKoin
而不是 org.koin.java.standalone.KoinJavaStarter.startKoin
。
由于我的应用程序class仍然是用Java编写的,我用一种方法创建了一个名为KoinHelper的对象:
@JvmStatic
fun start(application: Application) {
application.startKoin(application, listOf(AppModule.appModule))
}
然后我从我的应用程序 onCreate
方法中调用了它 class:
KoinHelper.start(this);
现在,我在原始答案中发布的仪器测试运行得很好。
请参阅 this issue on GitHub 了解更多信息。
@Before
fun setUp() {
stopKoin()
startKoin {
androidContext(app) // for example ApplicationProvider.getApplicationContext<TestApplication>()
modules(module1, module2)
}
}
我的一个 classes 具有上下文类型的依赖项。在将 Koin 添加到我的项目之前,我通过对我的应用程序 class:
的硬依赖来初始化它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()) }
}
}
我在我的应用程序 class 的 onCreate
方法中启动 Koin(在 Java 中编写):
startKoin(singletonList(AppModule.appModule));
我想用仪器测试而不是单元测试来测试这个 class,因为我想使用真实的上下文而不是模拟。这是我的测试:
@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)
}
我怎样才能使它作为具有真实上下文的仪器测试工作?
代替(在申请中):
startKoin(applicationContext, modules)
使用模拟上下文:
startKoin(modules) with (mock(Context::class.java))
请检查文档中的 this 部分。它说
if you need to start Koin from another Android class, you can use the startKoin() function and provide your Android Context instance with just like:
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
关于在插桩测试中获取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
子类中创建的现有模块。 ¯\_(ツ)_/¯
显然无法从 Java class 启动 Koin 并注入应用程序上下文。这意味着如果您的 classes 之一需要从容器中获取上下文,则必须使用 org.koin.android.ext.android.startKoin
而不是 org.koin.java.standalone.KoinJavaStarter.startKoin
。
由于我的应用程序class仍然是用Java编写的,我用一种方法创建了一个名为KoinHelper的对象:
@JvmStatic
fun start(application: Application) {
application.startKoin(application, listOf(AppModule.appModule))
}
然后我从我的应用程序 onCreate
方法中调用了它 class:
KoinHelper.start(this);
现在,我在原始答案中发布的仪器测试运行得很好。
请参阅 this issue on GitHub 了解更多信息。
@Before
fun setUp() {
stopKoin()
startKoin {
androidContext(app) // for example ApplicationProvider.getApplicationContext<TestApplication>()
modules(module1, module2)
}
}