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 1.0] Testing improvements with declareMock() & declare() #151

Closed
arnaudgiuliani opened this issue Jun 19, 2018 · 9 comments
Closed
Labels
Milestone

Comments

@arnaudgiuliani
Copy link
Member

arnaudgiuliani commented Jun 19, 2018

Hello,

koin-test will no rely on mockito to provide out of the box mocking.

You can now directly declare a mock from a KoinTest file with declareMock():

val mod = module {
                single { ComponentA() }
                single { ComponentB(get()) }
            }
@Test
    fun `successful override factory with a mock`() {
        startKoin(listOf(mod))
        
       // will override ComponentA with mock definition
        declareMock<ComponentA>()
    }

You can also declare some components with declare { }, if you need more complex expression:

@Test
    fun `successful override factory with a mock`() {
        startKoin(listOf(mod))
        
        // will override ComponentA with mock definition
        declare { factory { DumbComponentA() } }
    }

No need to declare a special module, if you have only local expressions/mocks.

Available in koin-test version 1.0.0-beta-1.

@patrice-conil
Copy link

Nice,
I 'll play with it.
Patrice

@patrice-conil
Copy link

Hi @arnaudgiuliani
I played a bit with it ... and discovered that I haven't understood very well the usage of bind.

Writing two tests (first fails, second succeeds)

   @Test
    fun `successful override with an interface mock`() {
        startKoin(listOf(
                module {
                    single { ComponentA() } bind InterfaceA::class
                    single { ComponentB(get()) }
                }
        ))

        createMock<InterfaceA>()

        val mockA = get<InterfaceA>()
        Assert.assertEquals(mockA, get<ComponentB>().componentA)

        assertDefinitions(2)
        assertRemainingInstances(2)
    }

    @Test
    fun `successful override with an interface mock 2`() {
        startKoin(listOf(
                module {
                    single { ComponentA() as InterfaceA }
                    single { ComponentB(get()) }
                }
        ))

        createMock<InterfaceA>()

        val mockA = get<InterfaceA>()
        Assert.assertEquals(mockA, get<ComponentB>().componentA)

        assertDefinitions(2)
        assertRemainingInstances(2)
    }

I found a way to succeed but is-it an undocumented feature(AKA bug) or the behavior you expect ?
Patrice

@arnaudgiuliani
Copy link
Member Author

arnaudgiuliani commented Jun 21, 2018

Thanks for your feedback!

Yeah, it's because val mockA = get<InterfaceA>() will just declare a single { mock<InterfaceA>() } and not single { ... } bind InterfaceA::class

I've added a fix. To override your definition single { ComponentA() } bind InterfaceA::class, you will have to write createMock<ComponentA>(binds = listOf(InterfaceA::class))
The fix add the possibility to put some classes in binds section of the definition.

@arnaudgiuliani
Copy link
Member Author

since 1.0.0-alpha-29

@patrice-conil
Copy link

Nice and clear,
I had to revert to kotlin 1.2.41 in a local version of koin to deal with android binding bug https://issuetracker.google.com/issues/110198434
I continue my investigations.
Patrice

@arnaudgiuliani arnaudgiuliani changed the title [Koin 1.0] Testing improvements with createMock() & declare() [Koin 1.0] Testing improvements with declareMock() & declare() Jun 27, 2018
@patrice-conil
Copy link

Thanks Arnaud,
Now we have an nice way to do what we want
I wrote a sample project using createMock that you can find here https://github.com/patrice-conil/testKoinMockBean
Patrice

@arnaudgiuliani
Copy link
Member Author

Cool :)
I' had to rename it declareMock to not conflict with the createMock() from mockito.

@patrice-conil
Copy link

Sample updated with declareMock.
Now , I'm waiting for the release ;)

@Liako
Copy link

Liako commented Nov 29, 2021

Hello @arnaudgiuliani i am facing aproblem i have updated to the latest V3 and my test are not running because of the declare method. I am attaching you the code currently using

fun createUserScopeFor(username: String): Scope {
koin.getScopeOrNull(Scopes.USER_SESSION)?.close()
return koin.createScope(Scopes.USER_SESSION, named(Scopes.USER_SESSION))
.apply {
declare(UserIdentifier(username))
get().create(this)
}
.also(::updateTrackingState)
}

and then trying to run a RxChain for a purpose when it reaches the create userScope it throughs a null pointer which comes when the code riches the declare function.

My test is like this
@test
fun login completes successfully when all requests succeed() {
val builder = ArrangeBuilder().withAllRequestsSucceeding()

        val subscription = useCase.credentialsLogin(USERNAME, PASSWORD, true).test()
        testScheduler.triggerActions()

        subscription.assertValue { it.isEnrolled() }
        verify(builder.appUpdateService).appUpdateCheck(any())
        verify(builder.tokenService).login(any(), any())
    }

And i am mocking the koin thingies like follow

whenever(koin.createScope(Scopes.USER_SESSION, named(Scopes.USER_SESSION))).thenReturn(
userScope
)

where user scope is mock() and
whenever(userScope.get()).thenReturn(mock())
When i run the test i am getting something like
Screenshot 2021-11-29 at 12 06 30

If i memove the declare(UserIdentifier(username)) then the test is running properly. Could you please provide me with some hints how this could be done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants