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

Multiple bean definitions (Conflicts) #100

Closed
wcarmon opened this issue Mar 30, 2018 · 10 comments
Closed

Multiple bean definitions (Conflicts) #100

wcarmon opened this issue Mar 30, 2018 · 10 comments
Milestone

Comments

@wcarmon
Copy link
Contributor

wcarmon commented Mar 30, 2018

Your documentation says "latest bean wins"
(See https://insert-koin.io/docs/1.0/reference/koin-dsl/?#multiple-definitions)

The source code throws an exception if there are multiple beans:
see https://github.com/Ekito/koin/blob/0.9.1/koin-core/src/main/kotlin/org/koin/KoinContext.kt#L121

In case future readers face this,

My use case:
I want to override/replace/shadow specific beans with mocks/spies in my unit/integration tests.

My solution was to

  • Register all the normal application beans (eg. via startKoin)
  • Create a BeanDefinition instance for each mock/spy/fake
  • Register them manually koin.beanRegistry.declare( theBeanDef , Scope.root())
@arnaudgiuliani
Copy link
Member

If you want to reuse a module and override some definitions for your test, just make a module containing your new definitions and load modules in order: your app + your test module

The last module will override needed definitions

A bean is overriden when: same name, type and scope

You can match multiple bean for a given type if you use bind oeprators then

@arnaudgiuliani
Copy link
Member

@wcarmon
Copy link
Contributor Author

wcarmon commented Apr 4, 2018 via email

@arnaudgiuliani
Copy link
Member

Do you have any sources to provide?

@arnaudgiuliani
Copy link
Member

Could help me better understand your case

@wcarmon
Copy link
Contributor Author

wcarmon commented Apr 4, 2018

It's really hard to extract out a MWE for this.

I think I can illustrate using the sample app you provided...

Given this line...

You have testApp, which is a list (collection) of modules, and you pass that to startKoin in your TestApplication.

Unlike in this sample, My TestApplication had something like

startKoin( modulesForRealApp + aFewMockedInstancesInAModule )

There was overlap between the classes in modulesForRealApp and the classes in aFewMockedInstancesInAModule (eg. Same class, but in aFewMockedInstancesInAModule, I used Mockito.mock( MyClass ))

startKoin would fail with this exception.

My workaround was basically this

val koin = StandAloneContext.startKoin(
                list = RealApplication.koinModules.toList(),
                ...other params...
        )

val mocks: Collection<BeanDefinition<*>> = ...build-BeanDefinition-instances-manually...

mocks.forEach {
        koin.beanRegistry.declare(it, Scope.root())
}

@arnaudgiuliani
Copy link
Member

If I understand, if you provide a dedicated mock module aFewMockedInstancesInAModule, it fails?
If you run it in a KoinTest, everything is still lazy until you pull any instance.

Or do you do something in some constructors?

@wcarmon
Copy link
Contributor Author

wcarmon commented Apr 5, 2018

Correct, startKoin fails when I add a dedicated module containing mock instances. (Only when 1 or more mocks instances replace a real instance. If there's no collision, there's no exception)

I did not use KoinTest. (I need to extend an unrelated class so extending KoinTest wasn't an option for me)

All of my injection is eager & constructor based (except my Android activities, which use val ... by inject())

@arnaudgiuliani
Copy link
Member

I did not use KoinTest. (I need to extend an unrelated class so extending KoinTest wasn't an option for me)

KoinTest is just an interface. It help to tag a class and bring extenions on it.

All of my injection is eager & constructor based (except my Android activities, which use val ... by inject())

Original conflict comes from here. making it eager at start don"t help you, even more, if you use android context at start.

@wcarmon
Copy link
Contributor Author

wcarmon commented Apr 7, 2018

Of course you are correct about KoinTest being an interface. Clearly I didn't look into KoinTest at all.

As you mentioned, the root cause appears to be a choice of eager-injection versus lazy-injection.

I still prefer "immutable-after-construction" & eager injection, so I'll use the workaround above.

Thanks for the helpful information.

@arnaudgiuliani arnaudgiuliani added this to the 1.0.0 milestone Apr 16, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants