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

Add support for scope linking in checkModules #1306

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -27,6 +27,12 @@ class CheckModulesTest {
single { p -> Simple.ComponentB(p.get()) }
single(named("param")) { p -> Simple.MyString(p.get()) }
single { Simple.MyString(getProperty("aValue")) }
scope(named("scope1")) {
scoped { Simple.ComponentD() }
}
scope(named("scope2")) {
scoped { Simple.ComponentE(get()) }
}
}

koinApplication {
Expand All @@ -36,6 +42,7 @@ class CheckModulesTest {
// withInstance<String>("a_parameter")
withParameter<String> { "a_parameter" }
withProperty("aValue", "string_value")
withScopeLink(named("scope2"), named("scope1"))
}
}
}
Expand All @@ -46,12 +53,19 @@ class CheckModulesTest {
single { p -> Simple.ComponentB(p.get()) }
single(named("param")) { p -> Simple.MyString(p.get()) }
single { Simple.MyString(getProperty("aValue")) }
scope(named("scope1")) {
scoped { Simple.ComponentD() }
}
scope(named("scope2")) {
scoped { Simple.ComponentE(get()) }
}
}

checkKoinModules(listOf(modules)) {
withInstance<Simple.ComponentA>()
withParameter<String> { "a_parameter" }
withProperty("aValue", "string_value")
withScopeLink(named("scope2"), named("scope1"))
}
}

Expand Down Expand Up @@ -276,7 +290,7 @@ class CheckModulesTest {
}
)
}.checkModules()
fail("should not pass with borken definitions")
fail("should not pass with broken definitions")
} catch (e: Exception) {
e.printStackTrace()
}
Expand Down Expand Up @@ -504,4 +518,24 @@ class CheckModulesTest {
}
}
}

@Test
fun `check a module with linked scopes`() {
koinApplication {
printLogger(Level.DEBUG)
properties(hashMapOf("aValue" to "value"))
modules(
module {
scope<Simple.ComponentA> {
scoped { Simple.ComponentD() }
}
scope<Simple.ComponentB> {
scoped { Simple.ComponentE(get()) }
}
}
)
}.checkModules {
withScopeLink<Simple.ComponentB, Simple.ComponentA>()
}
}
}
Expand Up @@ -8,11 +8,14 @@ class Simple {
class ComponentA
class ComponentB(val a: ComponentA)
class ComponentC(val b: ComponentB)
class ComponentD()
class ComponentE(val d: ComponentD)
class MyString(val s: String)

class UUIDComponent {
fun getUUID() = UUID.randomUUID().toString()
}

}

object UpperCase : Qualifier {
Expand Down
Expand Up @@ -111,19 +111,23 @@ private fun Koin.declareParameterCreators(parametersDefinition: CheckParameters?

@OptIn(KoinInternalApi::class)
private fun Koin.checkAllDefinitions(allParameters: ParametersBinding) {
val scopes: List<Scope> = instantiateAllScopes(allParameters)
allParameters.scopeLinks.forEach { scopeLink ->
val linkTargets = scopes.filter { it.scopeQualifier == scopeLink.value }
scopes.filter { it.scopeQualifier == scopeLink.key }
.forEach { scope -> linkTargets.forEach { linkTarget -> scope.linkTo(linkTarget) } }
}
instanceRegistry.instances.values.toSet().forEach { factory ->
check(factory,allParameters)
checkDefinition(allParameters, factory.beanDefinition, scopes.first { it.scopeQualifier == factory.beanDefinition.scopeQualifier })
}
}

private fun Koin.check(
factory: InstanceFactory<*>,
allParameters: ParametersBinding
) {
val qualifier = factory.beanDefinition.scopeQualifier
val sourceScopeValue = mockSourceValue(qualifier)
val scope = getOrCreateScope(qualifier.value, qualifier, sourceScopeValue)
checkDefinition(allParameters,factory.beanDefinition,scope)
@OptIn(KoinInternalApi::class)
private fun Koin.instantiateAllScopes(allParameters: ParametersBinding): List<Scope> {
return scopeRegistry.scopeDefinitions.map { qualifier ->
val sourceScopeValue = mockSourceValue(qualifier)
getOrCreateScope(qualifier.value, qualifier, sourceScopeValue)
}
}

private fun mockSourceValue(qualifier: Qualifier): Any? {
Expand Down
Expand Up @@ -19,6 +19,7 @@ import org.koin.core.Koin
import org.koin.core.parameter.ParametersHolder
import org.koin.core.parameter.parametersOf
import org.koin.core.qualifier.Qualifier
import org.koin.core.qualifier.qualifier
import org.koin.mp.KoinPlatformTools
import org.koin.test.mock.MockProvider
import kotlin.reflect.KClass
Expand All @@ -29,6 +30,7 @@ class ParametersBinding(val koin: Koin) {

val parametersCreators = mutableMapOf<CheckedComponent, ParametersCreator>()
val defaultValues = mutableMapOf<String, Any>()
val scopeLinks = mutableMapOf<Qualifier, Qualifier>()

@Deprecated("use withParameter() instead", ReplaceWith("withParameters(qualifier,creator)"))
inline fun <reified T> create(qualifier: Qualifier? = null, noinline creator: ParametersCreator) =
Expand Down Expand Up @@ -62,6 +64,9 @@ class ParametersBinding(val koin: Koin) {
defaultValues.put(KoinPlatformTools.getClassName(T::class), MockProvider.makeMock<T>())

fun withProperty(key: String, value: Any) = koin.setProperty(key, value)
inline fun <reified T : Any, reified U : Any> withScopeLink() = withScopeLink(qualifier<T>(), qualifier<U>())
inline fun withScopeLink(scopeQualifier: Qualifier, targetScopeQualifier: Qualifier) =
scopeLinks.put(scopeQualifier, targetScopeQualifier)
}

typealias ParametersCreator = (Qualifier?) -> ParametersHolder
Expand Down
27 changes: 27 additions & 0 deletions docs/reference/koin-test/checkmodules.md
Expand Up @@ -226,3 +226,30 @@ fun `test DI modules`(){
}
}
```

#### Providing Scope Links

You can link scopes by using `withScopeLink` function in`checkModules` block to inject instances from another scope's definitions:

```kotlin
val myModule = module {
scope(named("scope1")) {
scoped { ComponentA() }
}
scope(named("scope2")) {
scoped { ComponentB(get()) }
}
}
```

```kotlin
@Test
fun `test DI modules`(){
koinApplication {
modules(myModule)
checkModules(){
withScopeLink(named("scope2"), named("scope1"))
}
}
}
```