Koin tests - DependencyResolutionException - android

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

Related

How to set an order in instrumented test over the Test Class?

I'd like to set an order over the Test Class.
#RunWith(AndroidJUnit4::class)
#FixMethodOrder(MethodSorters.NAME_ASCENDING)
class ATest {
#Test
fun test0000()
#Test
fun test0001()
}
#RunWith(AndroidJUnit4::class)
#FixMethodOrder(MethodSorters.NAME_ASCENDING)
class BTest {
#Test
fun test0002()
#Test
fun test0003()
}
I'd like to test ATest.test0000 -> ATest.test0001 -> BTest.test0002 -> BTest.test0003
Because ATest class must be tested before B Test.
How can I do that? Is it possible?
Firstly, I would recommend you to not have any dependencies in Tests.
i.e. Test A class and Test B class should run independently from each other.
This really helps when your application grows.
There should not be a condition that one test should run before another.
Only in a rare / genuine scenario we should have such dependency on sequence.
Because if you design your test with sequence related dependency then it will be difficult for you to maintain your test cases and it will get difficult when you follow Test Driven Development(TDD).
For above case, please try using SuiteClasses.
The SuiteClasses annotation specifies the Suite runner which test classes to include in this suite and in which order.
Please refer to the sample provided by Junit Team HERE

Koin Android Test

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

How to mock after setup dagger-android 2.15 when writing espresso tests?

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.

Mockito Stubbed Spy sometimes calls and sometimes does not call the spied object methods

QUESTION
In a #Test, how can I achieve both;
Call a real method from a Kotlin class under test and
stub the inner calls it does to other methods within such class under test.
SCENARIO
I am using the following libraries;
testCompile "com.nhaarman:mockito-kotlin:1.5.0"
testCompile "org.mockito:mockito-inline:2.12.0"
I also have a simple kotlin class in the form of
class MyClass() {
fun parentFunc() {
funA()
}
fun funA() {
//DOES SOMETHING WHICH I ASSUME IS IRRELEVANT FOR ANSWERING THE QUESTION
}
}
TESTING WITH A SPY
#Test
fun myTest() {
val myClassSpy = spy(MyClass())
Mockito.doNothing().`when`(myClassSpy.funA())
//Mockito.doNothing().whenever(myClassSpy.funA()) also throws the same error
myClassSpy.parentFunc()
verify(myClassSpy, times(1)).funA()
}
Which throws the error,
org.mockito.exceptions.misusing.UnfinishedStubbingException:
Unfinished stubbing detected here:
-> at com.nhaarman.mockito_kotlin.MockitoKt.doNothing(Mockito.kt:108)
E.g. thenReturn() may be missing.
Examples of correct stubbing:
when(mock.isOk()).thenReturn(true);
when(mock.isOk()).thenThrow(exception);
doThrow(exception).when(mock).someVoidMethod();
Hints:
1. missing thenReturn()
2. you are trying to stub a final method, which is not supported
3: you are stubbing the behaviour of another mock inside before 'thenReturn' instruction if completed
Another Test case;
#Test
fun myTest() {
val myClassSpy = Mockito.spy(MyClass())
myClassSpy.parentFunc()
verify(myClassSpy, times(1)).funA()
}
gives the following error:
Wanted but not invoked:
myClass.funA();
However, there was exactly 1 interaction with this mock:
myClass.parentFunc();
Also, anytime I attempt to use the debugger to call myClassSpy methods or something related to it, it throws the following error:
com.sun.jdi.InternalException : Unexpected JDWP Error: 41
I have attempted to use
Mockito.`when`(myClassSpy.funA()).then { }
Mockito.`when`(myClassSpy.funA()).thenAnswer { }
Mockito.`when`(myClassSpy.funA()).thenReturn(Unit)
TESTING WITH A MOCK
Mocking the whole class does not work in this case because it is a mock and does not call the real method under test:
#Test
fun myTest() {
val myMock: MyClass = mock()
myMock.parentFunc()
verify(myMock, times(1)).funA()
}
Same error:
Wanted but not invoked:
myClass.funA();
However, there was exactly 1 interaction with this mock:
myClass.parentFunc();
If I further call the real method it also shows the same wanted but not invoked myClass.funA(); error:
#Test
fun myTest() {
val myMock: MyClass = mock()
Mockito.`when`(myMock.parentFunc()).thenCallRealMethod()
myMock.parentFunc()
verify(myMock, times(1)).funA()
}
I also, tried opening MyClassbut threw the same errors.
Thus, how can I stub methods from a spy so that when I test methods from such spied object it does not propagate the call to other methods which I do not want to further mock.
Any help, suggestion, idea... in order to test these type of methods is highly appreciated.
for me mocking kotlin classes with mockito sometimes works and sometimes it does not, so what i usually do is extract an for that class, and that always works.
also mocks are for testing interactions so you should not mock a method on the same class that you are testing but a different class that you pass to the constructor (dependency injection) then you can inject a mock at test time and the correct dependency at production time.
class MyTestedClass(val funManager:MyUsedClass) {
fun parentFunc() {
funManager.funA()
}
}
Solution:
When the code base is all in Kotlin the solution was:
Use MockK which is a library for testing and mocking Kotlin Code.
When the code base is written in both Java and Kotlin the solution was:
Use MockK for testing & mocking Kotlin and Mockito for testing & mocking Java code.

Integrating Robolectric and Cucumber

I want to combine both Robolectric and Cucumber (JVM).
Currently I have two classes ActivityStepdefs where two step definitions for activity management are defined.
My second class is RoActivity Where for example an activity is created from it's class name, and where Robolectric will be used.
When I run RoActivityTest using RobolectricTestRunner the test in this class passes, but when I run RunCukesTest (class for running features as junit test) the code from RoActivity is not running as part of Robolectric, i.e. RunCukesTest search for features on my project and match it with a method inside ActivityStepdefs and finally this class will call a method from RoActivity
Is possible to run test with both junit both* runners?
I'm not sure but perhaps it's possible to do something like powermock, using junit rules.
In that case for which one should I have to define the rule?
*Cucumber and Robolectric
My small 5 cents.
Cucumber is mostly used for acceptance tests (correct me if you use it for unit testing) and Robolectric is mostly used for unit testing.
As for me, it is overkill to write cucumber during TDD. And Robolectric is still not android and I would run acceptance tests on real device or at least emulator.
I'am facing the same problem, after some google work, I got a solution:
#RunWith(ParameterizedRobolectricTestRunner::class)
#CucumberOptions( features = ["src/test/features/test.feature","src/test/features/others.feature"], plugin = ["pretty"])
class RunFeatures(val index: Int, val name:String) {
companion object {
#Parameters(name = "{1}")
#JvmStatic
fun features(): Collection<Array<Any>> {
val runner = Cucumber(RunFeatures::class.java)
Cucumber()
val children = runner.children
return children.mapIndexed{index, feature ->
arrayOf(index,feature.name)
}
}
}
#Test
fun runTest() {
val core = JUnitCore()
val feature = Cucumber(RunFeatures::class.java).children[index]!!
core.addListener(object: RunListener() {
override fun testFailure(failure: Failure?) {
super.testFailure(failure)
fail("$name failed:\n"+failure?.exception)
}
})
val runner = Request.runner(feature)
core.run(runner)
}
}
but seems not an pretty solution for me, can somebody help me out these problem:
must explicitly list all feature file path. but cannot use pattern such as *.feature
when failed cannot know which step failed.
parameter can only pass primitive type data,
I've get into cucumber source , but seems CucumberOptions inline Cucumber , I cannot pass it programmatically but can only use annotation .

Categories

Resources