Kotlin - Mockito verify method calls - android

I'm trying my hand with Mockito for writing unit test's. I have a class that needs to be tested like below-
open class Employee {
fun setDetails(name: String, age: Int) {
setName(name)
setAge(age)
}
fun setName(name: String) { }
fun setAge(age: Int) { }
}
Below is my test class
class EmployeeTest {
#Mock
lateinit var emp: Employee
#Before
fun setup() {
MockitoAnnotations.initMocks(this)
}
#Test
fun testDetail() {
emp.setDetails("Henry", 23)
verify(emp, times(1)).setAge(23)
}
}
Here is my problem
When I do -
verify(emp, times(1)).setAge(23)
This give's me a success, because setAge is called once in setDetails() of Employee.kt. So that works fine for me
But, when I do-
verify(emp, never()).setAge(23)
This still gives me a success, even though the method is called in setDetails(). Shouldn't this test case fail?
Please help me understand this. I haven't been able to figure out why this happens.
EDIT
Here's what worked for me
I used a spy instead of a mock. However, I had to also declare the methods as open in Kotlin.

As mentioned by #kcoppock, your question includes an improper use of a mock. You should be using mocks to stub out dependencies in order to control their behavior.
In your case, the unit under test is the Employee class and its associated methods. In general, you do not want to mock out the unit under test because you want to know (from your unit test) if your class is behaving the way it should. To accomplish that, you'll want to use a real instance of an Employee, and not a mock.
If you are insistent on using verify on the Employee instance, you can create a spy.
#Test
fun setDetails_adjustsAge() {
val employee = spy(Employee())
employee.setDetails("Henry", 23)
assertEquals(23, employee.age)
verify(emp, times(1)).setAge(23)
}
Here are some references for further reading:
Mockito official documentation on spies:
http://static.javadoc.io/org.mockito/mockito-core/2.24.0/org/mockito/Mockito.html#13
Tutorial on how to use Mockito.spy
https://www.baeldung.com/mockito-spy
Differences between mock and spy: https://www.toptal.com/java/a-guide-to-everyday-mockito

So your issue here is that you don't actually want to use a mock. When you use a mock, you're required to define the behavior for any method that you call on that instance. So when you call emp.setDetails("Henry", 23), there is no implementation for that method so nothing happens. The behavior defined in the Employee class will not be used, as emp is just a fake instance of Employee that has not defined any behavior.
For your test scenario, you should prefer to use a real instance, and validate the end result rather than the internal behavior. For instance:
#Test
fun setDetails_adjustsAge() {
val employee = Employee()
employee.setDetails("Henry", 23)
assertEquals(23, employee.age)
}

Related

How to write test cases for class using netrypoint

I am evaluating if I can use hilt in my app. One blocker is to understand how to write test cases for class that is using entry points. I am not able to find anything related to it. Below is an example of a class method that I would like to unit test but I am not able to understand how to do it. Please help.
suspend fun processResponse(context: Context, requestType: String, response: NetworkResponse) {
val obj = EntryPointAccessors.fromApplication(
appContext, UtilitiesEntryPoint::class.java)
someDependency = obj.getSomeDependency()
//some business logic that need to be tested

Unable to test kotlin interface with Robolectric

Currently, I am using a testing framework called Robolectric. And I was wondering what is the right way to test an interface to check the value and method for it. For example, In the following sample, I have an interface called TestInterface. And I wanted to test if this interface will work. So I set up like the following, by making the interface into an object. Is it a wrong way to do it ? Or is there a better approach? I'm not savvy with testing. So if there are some samples or hints, it will be so helpful. Thank you.
#RunWith(RobolectricTestRunner::class)
class MySampleTest {
#Test
fun `test mysample `() {
val testName = "Test Name"
val userInput = "User Input"
val testResults = object : TestInterface {
override val testValue: String
get() = testName
override fun createTest(input: String): String {
return userInput+input
}
}.createTest(userInput)
assertTrue(keyPair.verify(userInput, testResults))
}
As we are not talking about UI, saying "test interface" confuses me. We can write tests for implementations only. Otherwise what we can test at all?
There should be one or more implementations in you project (if don't, why do even need this interface?), so you can test them.

Android SparseArray is null when running a Unit Test

I have a unit test for a kotlin object which uses a SparseArray.
The test always failed because the SparseArray is always null despite it's initialization.
object Exam : KoinComponent {
var map = SparseArray<Char?>()
init {
map.put(0, 'a')
map.put(1, 'b')
map.put(2, 'c')
map.put(3, 'd')
}
fun getChar(key: Int): Char? {
Log.d(KOIN_TAG, "" + map.get(key))
return map.get(key)
}
class ExamTest : KoinTest {
#Test
fun getCharTest(){
assertEquals(Exam.getChar(0), 'a')
}
}
I debugged this test and it ran through the init of the Array, but the value is always null.
Please help me to solve this case. Thank you
The problem is, that SparseArray is part of Android and not of Java. In an unit test you are only able to use Plain old Java Objects and no android classes or dependencies (for example from package android.util).
Therefore you have to mock the SparseArray in your test.
For example you can use Mockito:
private var map: SparseArray<Char> = SparseArray()
map = mock()
Unfortunately you are not able to pass the mocked sparse array to your object.
So you cannot test your specific example like this.
Another solution would be to use the Robolectric framework if you want to test a class with platform dependencies. http://robolectric.org/

Unit Testing AndroidViewModel classes

Im writting Unit Tests for my app and I've found a "speed bump" while writting them. While testing subclasses of AndroidViewModel im missing the Application parameter for its initialization. I've already read this question that uses Robolectric.
This is what I already tried so far:
Using Robolectric as the question describes. As far as I understand, Robolectric can use your custom Application class for testing, the thing is im not using a custom application class as I dont need it. (the app is not that complex).
Using mockito. Mockito throws an exception saying that the Context class cannot be mocked.
Using the InstrumentationRegistry. I moved the test classes from the test folder to the androidTest folder, giving me access to the androidTestImplementation dependencies, I tried using the InstrumentationRegistry.getContext() and parse it to Application, of course this didn't worked, throwing a cast exception. I felt so dumb trying this but again, it was worth the shot.
I just want to instanciate my AndroidViewModel classes so I can call their public methods, but the Application parameter is needed. What can I do for this?
fun someTest() {
val testViewModel = MyViewModelThatExtendsFromAndroidViewModel(**missing application parameter**)
testViewModel.foo() // The code never reaches here as the testViewModel cant be initializated
}
I had the same issue, and found two solutions.
You can use Robolectric in a Unit Test, inside test directory, and choose the platform Application class.
#RunWith(RobolectricTestRunner::class)
#Config(application = Application::class)
class ViewModelTest {
#Test
#Throws(Exception::class)
fun someTest() {
val application = RuntimeEnvironment.application
val testViewModel = MyViewModelThatExtendsFromAndroidViewModel(application)
testViewModel.foo()
}
}
Or you can use an InstrumentationTest inside androidTest directory, and cast the InstrumentationRegistry.getTargetContext().applicationContext to Application:
#RunWith(AndroidJUnit4::class)
class ViewModelTest {
#Test
#Throws(Exception::class)
fun someTest() {
val application = ApplicationProvider.getApplicationContext() as Application
val testViewModel = MyViewModelThatExtendsFromAndroidViewModel(application)
testViewModel.foo()
}
}
Hope it helped!

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.

Categories

Resources