Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[koin-android-compose] Broken view model factory when migrating from 3.5.0 -> 3.5.3/3.5.4 #1844

Closed
javiercamarenatriguero opened this issue Apr 8, 2024 · 13 comments

Comments

@javiercamarenatriguero
Copy link

javiercamarenatriguero commented Apr 8, 2024

Describe the bug
I recently upgraded to koin-androidx-compose:3.5.4 from koin-androidx-compose:3.5.0 and the Automation Test stat failing because of this error:

Caused by: org.koin.core.error.ClosedScopeException: Scope '_root_' is closed
        at org.koin.core.scope.Scope.resolveInstance(Scope.kt:224)
        at org.koin.core.scope.Scope.get(Scope.kt:213)
        at org.koin.androidx.viewmodel.factory.KoinViewModelFactory.create(KoinViewModelFactory.kt:25)
        at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:184)
        at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:150)
        at org.koin.androidx.viewmodel.GetViewModelKt.resolveViewModel(GetViewModel.kt:44)

Tested it just affect to koin-android-compose library as the rest of koin libraries are not affecting the Android Test results.
To Reproduce
Steps to reproduce the behavior:

  1. Run Automation Test with more than 1 test cases
  2. See error after the second TestCase starts the execution.

Expected behavior
It should not fail as v3.5.0 version.

Koin module and version:
koin-androidx-compose:3.5.3 & koin-androidx-compose:3.5.4

@arnaudgiuliani
Copy link
Member

Can you detail it a bit more?

@arnaudgiuliani arnaudgiuliani added type:issue status:checking currently in analysis - discussion or need more detailed specs labels Apr 10, 2024
@javiercamarenatriguero
Copy link
Author

Thanks @arnaudgiuliani ! Sure, The issue just affects when the koin-androidx-compose is upgraded from v3.5.0 -> v3.5.3/4. The code contains some Automation Test in Espresso, and for some reason the second Test Case of the same Test Class fails when the ViewModelFactory tries to get the instance of the ViewModel. In order to provide more information, this is the KoinTestRule used on the Test Class (based on tutorials):

class KoinTestRule(
    private val modules: List<Module> = emptyList()
) : TestWatcher() {

    private val modulesByDefault = listOf(dataModule, useCaseModule, viewModelModule, appModule)
    override fun starting(description: Description) {
        startKoin {
            androidContext(InstrumentationRegistry.getInstrumentation().targetContext.applicationContext)
            modules(modulesByDefault + modules)
        }
    }

    override fun finished(description: Description) {
        stopKoin()
    }
}

And this is the invocation on the Test Class:

private val instrumentedTestModule = module {
        factory { getMockDataLocalSource() }
        factory { getMockQuestions(questionJson) }
        ...
    }

    override val koinTestRule: KoinTestRule
        get() = KoinTestRule(
            modules = listOf(instrumentedTestModule)
        )

The first TestCase works fine, but the second one fails on this. It happens to all the Test Classes. It was working fine until now. Thanks in advance

@kasim-canol-sz
Copy link

@arnaudgiuliani Do you have any updates regarding this issue? Our tests are also affected by the same issue, therefore we're currently not able to update the koin version.

@javiercamarenatriguero
Copy link
Author

Hi @arnaudgiuliani, any update about this issue? Thanks in advance

@arnaudgiuliani
Copy link
Member

weird, seems that VM factory is invoked after scope closing 🤔

@arnaudgiuliani
Copy link
Member

can anyone help by providing a test project for that? @kasim-canol-sz @javiercamarenatriguero

@dazza5000
Copy link

We are experiencing this after upgrading too. Did anyone find a workaround?

@GerardPaligot
Copy link

I've the exact same issue. I can work on a small test project if it is necessary, but the issue is present in one of my OSS project (https://github.com/GerardPaligot/conferences4hall/tree/test/schedules). In this project, I'm using koin 3.6.0-Beta4 and I'm writing instrumented test cases in theme-m3/schedules/schedules-sample module with two test classes: FilteringScheduleTest and ScheduleDetailsTest.

These two classes is using org.koin.test.KoinTestRule to override koin modules used in the MainActivity of the sample module.

@get:Rule
val koinTestRule = KoinTestRule.create {
    androidContext(InstrumentationRegistry.getInstrumentation().targetContext.applicationContext)
    modules(listOf(scheduleModule, instrumentedModule))
}

If I'm executing one by one test classes, there is no issue but if I'm merging these test classes in only one file or if I'm executing these classes in the same JVM (run at the package level), I got the error:

java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:590)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:886)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:580)
... 1 more
Caused by: org.koin.core.error.ClosedScopeException: Scope '_root_' is closed
at org.koin.core.scope.Scope.resolveInstance(Scope.kt:224)
at org.koin.core.scope.Scope.get(Scope.kt:213)
at org.koin.androidx.viewmodel.factory.KoinViewModelFactory.create(KoinViewModelFactory.kt:25)
at androidx.lifecycle.ViewModelProvider$Factory.create(ViewModelProvider.android.kt:158)
at androidx.lifecycle.viewmodel.ViewModelProviderImpl.getViewModel$lifecycle_viewmodel_release(ViewModelProviderImpl.kt:67)
at androidx.lifecycle.viewmodel.ViewModelProviderImpl.getViewModel$lifecycle_viewmodel_release$default(ViewModelProviderImpl.kt:47)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.android.kt:91)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.android.kt:109)
at org.koin.androidx.viewmodel.GetViewModelKt.resolveViewModel(GetViewModel.kt:44)
at org.gdglille.devfest.android.theme.m3.schedules.feature.ScheduleGridVMKt.ScheduleGridVM(ScheduleGridVM.kt:82)
at org.gdglille.devfest.android.theme.m3.schedules.feature.ScheduleGridAdaptiveKt$ScheduleGridAdaptive$1.invoke(ScheduleGridAdaptive.kt:39)
at org.gdglille.devfest.android.theme.m3.schedules.feature.ScheduleGridAdaptiveKt$ScheduleGridAdaptive$1.invoke(ScheduleGridAdaptive.kt:38)

It starts from ScheduleGridVM class that inject its ViewModel but it is probably because it is the entry point of the injection. I think the root cause is probably more the koin context closed after every test case but the same scope context reused between every test cases.

I hope it can help. Available to carry out further investigations.

@GerardPaligot
Copy link

GerardPaligot commented Aug 18, 2024

After some deeper investigations, it seems that LocalKoinScope local composition isn't updated between two test cases (when there are executed in the same process). This local is used by the function currentKoinScope and this function, by koinViewModel function. That is the root cause of this issue.

I don't have a fix but if you need a workaround, you can force the correct scope inside your koinViewModel call:

viewModel: ScheduleGridViewModel = koinViewModel(
    scope = KoinPlatformTools.defaultContext().get().scopeRegistry.rootScope
)

or use KoinContext composable at the root level of your component tree to force the scope update:

setContent {
  KoinContext(context = KoinPlatformTools.defaultContext().get()) {
    // Your composable tree
  }
}

I hope it can help someone here!

@javiercamarenatriguero
Copy link
Author

Hi there, after applying the given solution (setting the scope) it still fails on the second test:

java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:562)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
... 1 more
Caused by: org.koin.core.error.ClosedScopeException: Scope '_root_' is closed
at org.koin.core.scope.Scope.resolveInstance(Scope.kt:224)
at org.koin.core.scope.Scope.get(Scope.kt:213)

Hope it could help to find a solution.
Thanks in advance.

@arnaudgiuliani
Copy link
Member

Ok, let me check it again. I missed the issue 👍

@arnaudgiuliani arnaudgiuliani added this to the 4.0-RC3 milestone Sep 9, 2024
@arnaudgiuliani
Copy link
Member

After some deeper investigations, it seems that LocalKoinScope local composition isn't updated between two test cases (when there are executed in the same process). This local is used by the function currentKoinScope and this function, by koinViewModel function. That is the root cause of this issue.

I don't have a fix but if you need a workaround, you can force the correct scope inside your koinViewModel call:

viewModel: ScheduleGridViewModel = koinViewModel(
    scope = KoinPlatformTools.defaultContext().get().scopeRegistry.rootScope
)

or use KoinContext composable at the root level of your component tree to force the scope update:

setContent {
  KoinContext(context = KoinPlatformTools.defaultContext().get()) {
    // Your composable tree
  }
}

I hope it can help someone here!

Seems linked to recent

@arnaudgiuliani
Copy link
Member

let's follow up #1900 - Fix around base Scope API and Local Composition should do the thing.

@arnaudgiuliani arnaudgiuliani added status:duplicated and removed status:checking currently in analysis - discussion or need more detailed specs labels Sep 12, 2024
arnaudgiuliani added a commit that referenced this issue Sep 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants