I have a problem with Koin & "androidTest".
Because androidTest starts the Application i don't need to start Koin by myself in the test.
Now i need to inject a mock service. The problem is, that i inject inside of a method with get() inside of a singleton class and this is not working via constructor injection because the injected object can have different implementations.
My idea was to declare what i need this way:
declare {
factory<Webservice>(override = true) { mockWebservice }
}
But this will be applied on all tests. That's why an other test, which checks if the correct class was injected failed.
I also tried to use stopKoin(), startKoin(listOf(appModule)) in the #After method, but with this the dependency injection doesn't work anymore in later tests.
Is there a way to declare the mock only for one test?
Here is how I do it in my Android Tests:
In a parent test class, I use these methods for setup and teardown:
#Before fun startKoinForTest() {
if (GlobalContext.getOrNull() == null) {
startKoin {
androidLogger()
androidContext(application)
modules(appComponent)
}
}
}
#After fun stopKoinAfterTest() = stopKoin()
My appcomponent contains all modules needed for the dependency tree.
Then, when I want to mock a dependency for a specific test, I use something like this:
declareMock<TripApi> { given(this.fetch(any())).willReturn(TestData.usaTrip) }
You will need to add a new mock declaration for each test if you wish to swap a dependency with a mock.
To declare mock only for one test you can use loadKoinModules()
You can’t call the startKoin() function more than once. But you can use directly the loadKoinModules() functions.
So this way your definition will override default one
loadKoinModules(module {
factory<Webservice>(override = true) { mockWebservice }
})
Also, don't forget to implement KoinTest interface in you test class
Related
I have an AAR project, and i'm trying to integrate Koin (my first Koin project).
I'm following Koin instruction for context isolation (https://insert-koin.io/docs/reference/koin-core/context-isolation/). and i will be glad to get some help.
I need to register the koin context this way:
MyKoinContext.koinApp = KoinApp
I don't understand when should i call this line and what is the KoinApp (should be koinApplication()?)
The second part that i'm not sure is that i should configure KoinComponent:
abstract class CustomKoinComponent : KoinComponent {
// Override default Koin instance, initially target on GlobalContext to yours
override fun getKoin(): Koin = MyKoinContext.koinApp?.koin
}
but getKoin() should return Koin and not Koin? how sholud i changed it.
I have multiple test classes with multiple tests in each class.
In each class I want to make sure that I get fresh test dependencies for each test. So I prepare my tests like this:
#Before
fun initTest() {
loadKoinModules(listOf(module {
scope(TEST_SCOPE, override = true) { Dependency1() }
scope(TEST_SCOPE, override = true) { Dependency2() }
}))
getKoin().createScope(TEST_SCOPE)
}
#After
fun shutdown() {
getKoin().getScope(TEST_SCOPE).close()
}
And it works very well when I run only the tests in that particular test class.
But when I run all my tests in the same time, and if multiple test classes have the same dependencies in their modules, I get an Exception like this:
org.koin.error.DependencyResolutionException: Multiple definitions found - Koin can't choose between :
Scope [name='Dependency2',class='package.Dependency2']
Scope [name='Dependency2',class='package.Dependency2']
Check your modules definition, use inner modules visibility or definition names.
So I fixed this by simply calling stopKoin() at the end of my shutdown method.
And so far I haven't noticed that my tests run much slower. So basically my question is: is this the preferred way to use Koin in my tests? Am I missing something or not using Koin Properly ?
I realize that this is more a code review/advice question than a real problem but I think this might still be useful to others.
Thanks
If we just use plain dagger 2. In the application class, we will have a property which holds the AppComponent. Then we can swap it during espresso tests.
But when I setup my project using dagger-android 2.15. Things becomes more implicit if adopt too much Dagger magic. The code is more clean, but makes testing a little bit hard.
This is the application class:
class App : DaggerApplication() {
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerAppComponent
.builder()
.create(this)
.build()
}
}
This is the HomeActivity
class HomeActivity : DaggerAppCompatActivity() {
#Inject
lateinit var userPreference: UserPreference
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home)
if (!this.userPreference.memberRegistered) {
goToActivity(EntryActivity::class.java)
}
}
}
Take this code for example. How to mock that injected userPreference.memberRegistered Which could be a HTTP call underneath?
For those who is interested in this, I got a blog with step by step detail for this:
Basically, the idea is:
You still generate instances for injection in #Module
But We’ll create new #Component A only for testing
This #Component will have a method to get that #Module
During tests, we swap the #Component that the application use with our component A
Then things are easy:
Without DaggerMock
In the #Module, instead of return real instance, you just return mockito mock.
With DaggerMock
You declare the type you want to swap and mock it
You can then use the mock.
No need to change the #Module
It inspires by #AutonomousApps 's solution, but the differences are now you don't need to write the #Component, #Module for each test class.
After trying several approaches, this is the only one that worked for me.
I wrote a blog post that explains how to do this just yesterday: https://dev.to/autonomousapps/the-daggerandroid-missing-documentation-33kj
I don't intend to repeat the entire post for this answer (it's hundreds of words and lines of code to properly set up a test harness with Dagger), but to attempt to summarize:
Add a custom application class in the debug source set (I assume it would also work in the androidTest source set, but I have not tried this).
You also need to reference this application in a AndroidManifest.xml in the same source set.
Create a "Test component" in your androidTest class that extends from your production top-level component and build it.
Use that test component to inject your application, which means you've just replaced your entire Dagger dependency graph with a new one you've defined just for the test suite.
Profit.
I would like to inject mocked overrides into my Android instrumentation tests using Kodein. I don't know which is the optimal approach to do this. Here's what I have in mind:
My app uses a KodeinAware application class. The served Kodein instance holds all dependencies required by my app.
In my tests I would like to inject mocked overrides for specific dependencies to test behavior of the app in various situations.
Overrides should be different for each test, and should be injected before/while the test runs.
Is the configurable Kodein extension sensible in this situation, or is there a simpler, better suited approach (and if so, which)?
If your test is given a Kodein instance (meaning that it can use a different Kodein object than the one held by your Application), then the recommended approach is to create a new Kodein object that extends the one of the app and overrides all necessary bindings.
val testKodein = Kodein {
extend(appKodein())
bind<MyManager>(overrides = true) with singleton { mock<MyManager>() }
}
The configurable Kodein option is recommended only if you're using a static "one true Kodein". Using it prevents the possibility to run you're tests in parallel (because they all access the same Kodein instance), and forces you to clear the ConfigurableKodein between each tests and re-declare every time different overrides.
I am now using the ConfigurableKodein inside my custom App class.
class App : Application(), KodeinAware {
override val kodein = ConfigurableKodein()
override fun onCreate() {
super.onCreate()
// A function is used to create a Kodein module with all app deps.
kodein.addImport(appDependencies(this))
}
}
// Helper for accessing the App from any context.
fun Context.asApp() = this.applicationContext as App
Inside my AppTestRunner class, I declare the configuration to be mutable. That way I can reset it's configuration between each and every test.
class AppTestRunner : AndroidJUnitRunner() {
override fun callApplicationOnCreate(app: Application) {
app.asApp().kodein.mutable = true
super.callApplicationOnCreate(app)
}
}
I have created a JUnit rule that reset the dependency graph before every test.
class ResetKodeinRule : ExternalResource() {
override fun before() {
val app = InstrumentationRegistry.getInstrumentation().targetContext.asApp()
app.kodein.clear()
app.kodein.addImport(appDependencies(app))
}
}
In my tests I can now retrieve the App.kodein instance and inject mocks that override dependencies of the original graph. The only thing that needs to be guaranteed is that the tested activity is launched after configuring mocks, or behavior is not predictable.
I want my code to be testable and flexible and I cannot make a choice whether I need to pass all dependencies explicitly to a constructor of a presenter or it's better to pass only View interface to a constructor and inject all dependencies in it.
It is a desirable pattern.
Every time you have access to object constructor, you should inject dependencies in constructor or other methods. #Inject annotation is indended mainly to be used inside object, that are not created by you.
When injecting all dependencies in constructor, during tests you pass all your dependencies to your model during initialisation. Therefore every test might contain different dependencies and different instance of created class. That is also the aim of unit tests - provide a sandbox for every test.
It is also easier to mock dependencies with Mockito.
Remember, that in unit tests, you don't have dependency any framework configured. A unit test usually contains created model and nothing more. Everything must be created by you (or mocked by framework)
Here is a sample unit test that proves the statement above:
#RunWith(MockitoJUnitRunner.class)
public class GetAreasUseCaseTest {
#Mock ApiManager mApiManager;
#Mock DatabaseManager mDatabaseManager;
private GetAreasUseCase mGetAreasUseCase;
#Rule
public final RxSchedulersOverrideRule mOverrideSchedulersRule = new RxSchedulersOverrideRule();
#Before
public void setUp() {
mGetAreasUseCase = new GetAreasUseCase(mApiManager,
mDatabaseManager);
doReturn(Observable.empty())
.when(mDatabaseManager)
.insertAreas(any(Area.class));
}
#Test
public void testGetAreasUseCaseApiInteraction() throws Exception {
TestSubscriber<List<Area>> testSubscriber = new TestSubscriber<>();
setCorrectApiResponse();
boolean input = true;
Observable testedObservable = mGetAreasUseCase.build(input);
testedObservable.subscribe(testSubscriber);
verify(mApiManager).getAreas(anyLong());
}
}
As you can see the structure of the test is very clear. It is well known, what is mocked and which object is tested. You have the control of the behaviour of dependencies.
If you plan to do only instrumentation tests, then you are provided with the ApplicationContext in tests and Dagger is properly initialised. There is no difference here. Of course you might still emulate Module's and Component's behaviour and provide custom object's Mocks instead of real classes (Example)
its not a bad practice but its hard to change the code in later changes. what if you want to add a new dependency for presenter ? then you have to change almost everything in your code.
what you are going to do actually is called Dependency Injection and the best practice is to do Dependency Injection, is using libraries like Dagger.
i think Dagger is the most powerful Dependency Injection library up to now. you will write some methods for providing your dependencies and Dagger will provide them for you whenever you want them using a #inject Annotation. for a complete instruction see this link:
http://www.vogella.com/tutorials/Dagger/article.html