Skip to content

Instantly share code, notes, and snippets.

@Sloy
Last active July 19, 2018 09:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Sloy/b64226a64c86ece8af5225bf4ff9b0b3 to your computer and use it in GitHub Desktop.
Save Sloy/b64226a64c86ece8af5225bf4ff9b0b3 to your computer and use it in GitHub Desktop.
Reproducing issue with Koin shared state in Android Tests. Inspired by the Dagger 1 version: https://gist.github.com/Sloy/c45dc3291403e603b4b88755e4a67660
open class Counter(context: Context) {
open var count: Int = 0
open fun hit() = count++
}
/**
* In Android Tests, a single application instance is shared between test methods and classes. This means Koin singletons will be shared
* between tests, instead of being recreated each time. That can make tests non deterministic, depending on the execution order.
*
* Another issue exists when a dependency is overridden with [KoinTest.declareMock] or [KoinTest.declare]. Because the Koin state is shared
* the mock will also be injected in other tests that expect the real dependency instead of the mock.
*
* This tests checks that the Koin injections are reset between test methods, avoiding the mentioned issues.
* If that is not the case, the second test executed will fail because it will retain the singleton instance of Counter from the previous one.
*/
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class KoinSharedStateTest : KoinTest {
private val counter by inject<Counter>()
@Test
fun a_fakeCounter() {
declareMock<Counter>()
given(counter.count).willReturn(8)
assertEquals(8, counter.count)
}
@Test
fun b_hitCounter() {
counter.hit()
assertEquals(1, counter.count)
}
}
class MyApplication : Application() {
val mainModule = module {
single { Counter(get()) }
}
override fun onCreate() {
super.onCreate()
startKoin(this, listOf(mainModule))
}
}
@Sloy
Copy link
Author

Sloy commented Jul 19, 2018

Even more improved: Create a JUnit rule and apply it to all your tests. That way you make sure you never share Koin state between tests.

class ReloadKoinRule : MethodRule {
    override fun apply(statement: Statement, frameworkMethod: FrameworkMethod, testClassInstance: Any): Statement {
        return object : Statement() {
            override fun evaluate() {
                KoinLoader.reload(getTargetContext())
                statement.evaluate()
                KoinLoader.reload(getTargetContext())
            }
        }
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment