InsertKoinIO / koin

Koin - a pragmatic lightweight dependency injection framework for Kotlin & Kotlin Multiplatform
https://insert-koin.io
Apache License 2.0
8.98k stars 710 forks source link

`checkModules()` throws NoBeanDefFoundException when module contains a declaration with parameters #1710

Closed okycelt closed 6 months ago

okycelt commented 10 months ago

Describe the bug I have three classes, two of which are declared in a module. In test, I provide the third class mock using withInstance<Outsider>(). But for some reason, checkModules() throws NoBeanDefFoundException: No definition found for type 'com.test.di.Outsider'. Check your Modules configuration and add missing type and/or qualifier!. It seems to be related to the factory for First having a parameter. If I remove that parameter, checkModules() doesn't find any problems. Am I doing something wrong or is this a bug?

To Reproduce Just run the attached test.

Expected behavior checkModules() shouldn't find any problems.

Koin module and version: koin-android, koin-test-junit4 (koin-bom:3.5.0)

Snippet or Sample project to help reproduce

data class Outsider(val version: String)
data class First(val outsider: Outsider, val booleanParam: Boolean)
data class Second(val first: First)
val testModule = module {
  factory { (booleanParam: Boolean) ->
    First(
      outsider = get(),
      booleanParam = booleanParam
    )
  }

  factory {
    Second(
      first = get { parametersOf(true) }
    )
  }
}
class TestModuleTests {
  @get:Rule
  val mockProvider = MockProviderRule.create { clazz ->
    mockkClass(clazz, relaxed = true)
  }

  @Test
  fun checkModules() {
    koinApplication {
      modules(testModule)
      checkModules {
        withInstance<Outsider>()
        withParameters<First> { parametersOf(false) }
      }
    }
  }
}
okycelt commented 10 months ago

It's also reproducible in core/koin-test-junit4/src/test/kotlin/org/koin/test/CheckModulesTest.kt. If you add a boolean param to Simple.ComponentB the following test fails.

class Simple {
    class ComponentB(val a: ComponentA, val b: Boolean)
}

@Test
fun `check module - dependency and param in one class`() {
    val modules = module {
        factory { p -> Simple.ComponentB(get(), p.get()) }
    }

    koinApplication {
        modules(modules)
        checkModules {
            withInstance<Simple.ComponentA>()
            withParameter<Simple.ComponentB> { true }
        }
    }
}
okycelt commented 10 months ago

@arnaudgiuliani, would you mind taking a look?

arnaudgiuliani commented 9 months ago

yep 👍

arnaudgiuliani commented 7 months ago

It's a current limitation of checkModules. You can't mix withInstance and withParameter for now. The current design is done to either provider value or dependency.

I would suggest looking more at Module.verify() API

arnaudgiuliani commented 7 months ago

checkModules is too complex to cover easily the configuration check. This will be deprecated in 3.6

Take a look at:

val modules = module {
    factory { p -> Simple.MyComplexBool(get(), p.get()) }
}

modules.verify(extraTypes = listOf(Simple.ComponentA::class, Boolean::class))
arnaudgiuliani commented 6 months ago

Let open a new message thread if needed