Basically I still haven't found a better way of loading/ unloading modules for Single activity, Multi-Module Feature approach. Loading and deloading of modules in fragments attach and detach works well, until it comes to UI testing. Then it is impossible to swap out the modules to test modules. This indeed works perfectly, but I'd like to be able to swap in and out the modules for testing somehow.
I was using this approach in my fragment
private val moduleList = arrayListOf(dataModule, viewmodelModule)
private val loadModules by lazy {
loadKoinModules(moduleList)
}
private fun injectFeatures() = loadModules
// Loading unloading modules
override fun onAttach(context: Context) {
super.onAttach(context)
injectFeatures()
}
override fun onDetach() {
unloadKoinModules(moduleList)
super.onDetach()
}
And in my tests
@Before
fun setUp() {
loadKoinModules(listOf(testViewModelModule, testDataModule))
launchFragmentInContainer<MenuFragment>()
}
val dataModule = module {
single<IMenuRepository> { ImplMenuRepository(get()) }
single<NetworkDataSource> { FirebaseDataSource(get()) }
}
val testDataModule = module(override = true) {
single<IMenuRepository>(override = true) { FakeMenuRepository() }
}
And this is my error
java.lang.RuntimeException: org.koin.core.error.DefinitionOverrideException: Definition '[Single:'com.nikolam.menu.data.IMenuRepository']' try to override existing definition. Please use override option or check for definition '[Single:'com.nikolam.menu.data.IMenuRepository']'
at androidx.test.runner.MonitoringInstrumentation.runOnMainSync(MonitoringInstrumentation.java:441)
at androidx.test.core.app.ActivityScenario.onActivity(ActivityScenario.java:564)
at androidx.fragment.app.testing.FragmentScenario.internalLaunch(FragmentScenario.java:300)
at androidx.fragment.app.testing.FragmentScenario.launchInContainer(FragmentScenario.java:282)
at com.nikolam.menu.ui.menu.MenuItemsDisplayTest.setUp(MenuItemsDisplayTest.kt:66)
at java.lang.reflect.Method.invoke(Native Method)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at androidx.test.internal.runner.junit4.statement.RunBefores.evaluate(RunBefores.java:76)
at androidx.test.internal.runner.junit4.statement.RunAfters.evaluate(RunAfters.java:61)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at androidx.test.internal.runner.TestExecutor.execute(TestExecutor.java:56)
at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:388)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2189)
Caused by: org.koin.core.error.DefinitionOverrideException: Definition '[Single:'com.nikolam.menu.data.IMenuRepository']' try to override existing definition. Please use override option or check for definition '[Single:'com.nikolam.menu.data.IMenuRepository']'
at org.koin.core.scope.ScopeDefinition.save(ScopeDefinition.kt:25)
at org.koin.core.scope.ScopeDefinition.save$default(ScopeDefinition.kt:19)
at org.koin.core.registry.ScopeRegistry.mergeDefinitions(ScopeRegistry.kt:95)
at org.koin.core.registry.ScopeRegistry.declareDefinitions(ScopeRegistry.kt:85)
at org.koin.core.registry.ScopeRegistry.declareScope(ScopeRegistry.kt:75)
at org.koin.core.registry.ScopeRegistry.loadModule(ScopeRegistry.kt:64)
at org.koin.core.registry.ScopeRegistry.loadModules$koin_core(ScopeRegistry.kt:55)
at org.koin.core.Koin.loadModules(Koin.kt:316)
at org.koin.core.context.ContextFunctionsKt.loadKoinModules(ContextFunctions.kt:60)
at com.nikolam.menu.ui.menu.MenuFragment$loadModules$2.invoke(MenuFragment.kt:28)
at com.nikolam.menu.ui.menu.MenuFragment$loadModules$2.invoke(MenuFragment.kt:22)
at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
at com.nikolam.menu.ui.menu.MenuFragment.getLoadModules(Unknown Source:2)
at com.nikolam.menu.ui.menu.MenuFragment.injectFeatures(MenuFragment.kt:31)
at com.nikolam.menu.ui.menu.MenuFragment.onAttach(MenuFragment.kt:79)
at androidx.fragment.app.Fragment.performAttach(Fragment.java:2673)
at androidx.fragment.app.FragmentStateManager.attach(FragmentStateManager.java:263)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1182)
at androidx.fragment.app.FragmentTransition.addToFirstInLastOut(FragmentTransition.java:1255)
at androidx.fragment.app.FragmentTransition.calculateFragments(FragmentTransition.java:1138)
at androidx.fragment.app.FragmentTransition.startTransitions(FragmentTransition.java:136)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2001)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1959)
at androidx.fragment.app.FragmentManager.execSingleAction(FragmentManager.java:1830)
at androidx.fragment.app.BackStackRecord.commitNow(BackStackRecord.java:297)
at androidx.fragment.app.testing.FragmentScenario$1.perform(FragmentScenario.java:317)
at androidx.fragment.app.testing.FragmentScenario$1.perform(FragmentScenario.java:301)
at androidx.test.core.app.ActivityScenario.lambda$onActivity$2$ActivityScenario(ActivityScenario.java:551)
at androidx.test.core.app.ActivityScenario$$Lambda$4.run(Unknown Source:4)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:462)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at android.app.Instrumentation$SyncRunnable.run(Instrumentation.java:2207)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
If anyone has had success with Koin and multi feature module projects, please fill me in. Any input would be really helpful!