Android - mocking issue - android

I have a custom class:
class MyClass {
var name = ""
fun changeName(newName: String) {
name = newName
}
}
and my testing class:
#Test
fun testVerifyMock() {
val instance: MyClass = mock()
instance.changeName("newname")
Assert.assertEquals("newname", instance.name)
}
I'm faily new to Unit Tests and I'm kinda stuck, can someone please point me to why I get this error:
java.lang.AssertionError:
Expected :newname
Actual :null
Basically the call instance.changeName("newname") doesn't seem to be changing the name since it's always null

Mockito mocks just ignore what you pass to their methods unless you explicitly tell them what to do. In the case of changeName, the parameter is just ignored and therefore the name will remain null. I don't see why you would use a mock here anyway, so just change to:
val instance = MyClass()
...
Here's a post on "when to use mock".

Related

Android MVVM testing with Mockito, mock repository.observeValue() has problem

I'm trying to test MVVM with Mockito.
The architecture of MVVM is similar to Android architecture blueprints.
We observe data from repository as LiveData.
And try to test observed value like below code.
class SplashViewModel(
private val appRepository: AppRepository
) {
val appInfo: LiveData<AppInfo> = appRepository.observeAppInfo()
}
#Test
fun getAppInfo() {
`when`(appRepository.observeAppInfo())
.thenReturn(appInfoData)
assertEquals(appInfoData, viewModel.appInfo.getOrAwaitValue())
}
The crucial point is viewModel.appInfo returns null, despite of I used mockito.
The problem
The creation of ViewModel is faster than using mockito.
So appInfo property is initialized with null, cause it dosen't know what observeAppInfo() is.
First solution
At first, I just trying to solve this problem with custom getter. Like this.
class SplashViewModel(
private val appRepository: AppRepository
) {
val appInfo: LiveData<AppInfo> get() = appRepository.observeAppInfo()
}
Now every time I access to appInfo they just re evaluate the data.
But is has it's own problem.
In this situation appVersion is getting error.
val appVersion: LiveData<String> = appInfo.map {
...
}
So every transformation LiveData(Like Transformations, MediatorLiveData) must use custom getter too.
And I felt it's not a great solution.
How do you think of it?
You could use #BeforeClass to make sure your initialisation is run before the tests.
#BeforeClass
fun setup(){
appInfo = appRepository.observeAppInfo()
}
See more

how to create unit test cases for kotlin data classes

I have created few data classes for my sample application. I need to write unit test cases for those data classes now. I am using Junit4.12. Here is my data class:
data class Tracking(val param1 : String?=null, val param2 : String?=null, val param3 : String?=null){}
I tried writing one basic unit test case for this model like below:
#Test
fun tracking()
{
val trackingData= Tracking("7030054",
"skdfksbfbkjsdf",
"dfkhsbfsjf")
Assert.assertEquals("true",trackingData.param1,"7030054")
}
But i don't see it is right way of performing a unit test case. My Objective for unit test case is to assert an exception if user sends null value as an input. Please help me out.
Your values are all String?, so nullable. If you want them to always be non-null, use String instead. Then you don't need a unit-test since non-nullability will be checked by the compiler.
If they can sometimes be null but not always, you have to first write a function that checks that condition.
A very simple example would be something like
fun simpleValidation() {
if(param1 == null) {
throw RuntimeException("Should not be null")
}
}
in the class tracking.
You can than unit test this by
#Test
fun tracking()
{
val trackingData= Tracking(null,
"skdfksbfbkjsdf",
"dfkhsbfsjf")
Assertions.assertThrows(RuntimeException::class.java) { trackingData.simpleValidation() }
}
assuming you are using JUnit5

JUnit test not sending values as parameters to function (Kotlin)

I am creating a simple junit test to test a function in my view model but the first assertion fails as the function I call returns null. When I debug the function I call has null parameters which is weird cause I pass them in.
I have spent time debugging and searching for why I am having that issue but I have found nothing that fixes my issue or tells me what the issue is.
#RunWith(MockitoJUnitRunner::class)
class CurrencyUnitTest {
#Rule
#JvmField
val rule = InstantTaskExecutorRule()
#Mock
val currencyViewModel : CurrencyViewModel = mock(CurrencyViewModel::class.java)
#Before
fun setUp() {
MockitoAnnotations.initMocks(this)
val rates: HashMap<String, Double> =
hashMapOf(
"USD" to 1.323234,
"GBP" to 2.392394,
"AUD" to 0.328429,
"KWR" to 893.4833
)
val currencyRates = MutableLiveData<Resource<CurrencyRatesData?>>()
val resource = Resource<CurrencyRatesData?>(Status.SUCCESS, CurrencyRatesData("CAD", rates, 0))
currencyRates.value = resource
`when`(currencyViewModel.currencyRatesData).thenReturn(currencyRates)
val baseCurrency = MutableLiveData<String>()
baseCurrency.value = "CAD"
`when`(currencyViewModel.baseCurrency).thenReturn(baseCurrency)
}
#Test
fun calculateValueTest() {
// this fails
assertEquals("0.36", currencyViewModel.calculateValue("AUD", "1.11"))
}
}
Mocked classes will not really be called. If you want to test your currencyViewModel.calculateValue() method, create a real object of that class and mock possible constructor arguments.
To add to what Ben has said: the class you want to test has to be a real object, not a mock. A mock "does nothing" per default, and only does what you do it to tell you, so to test it does not make any sense.
What you mock is the dependencies of the class you test, i.e. the objects you pass to its' constructor.
In short: if you want to test CurrencyViewModel, create an object of it instead of mocking it.

How to unit test retrofit call?

For Example I have a retrofit interface such as:
interface SampleService {
fun getSomething(#body someBody: SomeBody)
}
Now I have a class which uses this interface such as:
class UserRequester(val service: SampleService) {
fun doGetSomething(someValue: String) {
val response = service.getSomething(SomeBody(someValue))
// ...
}
}
I want to test this class but dont know how to mock it.
I'm trying the following:
val mockSampleService = mock()
val userRequester = UserRequester(mockSampleService)
val requestBody = SomeBody(someString))
when(mockSampleService.getSomething(requestBody)).return(myExpectedValue)
....
My problem is that since I create the request object inside the function, I could not make the mock when().thenReturn() to work since i am technically passing two different object.
How should I test this? Thanks in advance.
The mocking problem (UserRequester)
You are not able to mock the mockSampleService method because your class is creating the SomeBody object and is different from the SomeBody object you are creating in your test.
Now you have 2 options:
Use Mockito.any() in your test, in this way you basically say that whatever your method is gonna use as parameter you will return the mocked behaviour
Use a factory that given a someString returns you a SomeObject like this:
// the factory
class SomeObjectFactory{
fun createSomeObject(someString: String): SomeObject {
return SomeObject(someString)
}
}
//the class
class UserRequester(
val service: SampleService, val factory: SomeObjectFactory
) {
fun doGetSomething(someValue: String) {
val response = service.getSomething(factory.createSomeObject(someValue))
// ...
}
}
//the test
class MyTest{
#Test
fun myTestMethod(){
val mockSampleService = mock()
val factory = mock()
val someBody = mock()
val userRequester = UserRequester(mockSampleService, factory)
`when`(factory.createSomeObject(someString)).thenReturn(someBody)
`when`(mockSampleService.getSomething(someBody)).thenReturn(myExpectedValue)
//rest of the code
}
}
The second approach is the cleanest one.
Testing Retrofit calls (SampleService)
I wouldn't unit test a Retrofit call.
When you are dealing with frameworks, apis, databases, shared preferences is always preferable to do integration tests instead of unit tests.
In this way you are actually testing that your code is working with the outside world.
I suggest you to test Retrofit calls with MockWebServer (it's a library from Square, the same company that developed OkHttp and Retrofit).
This read may be also helpful.
Probably SomeBody is a plain value object, since Retrofit requests work with value objects. If you define the equals method for the SomeBody class then the eq matcher will work, and you can write using mockito-kotlin:
whenever(mockService.getSomething(eq(SomeBody(someString)))).thenReturn(stubbedResult)
Actually, you can omit the eq matcher, Mockito will use the equals method for matching.
If SomeBody is a Kotlin data class then the equals method is automatically defined by comparing the fields.
If for some reason you don't want to rely on equals, then you can use the argThat matcher defined in mockito-kotlin:
whenever(mockService.getSomething(argThat { theField == someValue })).thenReturn(stubbedResult)
The problem is that there is static dependency on SomeBody's constructor:
val response = service.getSomething(SomeBody(someValue))
What you could do to have control over the instantiation of SomeBody is to use a "provider" or "factory" object, you can inject it in the constructor and invoke it at the right time:
interface SampleService {
fun getSomething(someBody: SomeBody)
}
open class SomeBody(val body: String)
open class UserRequester(
val service: SampleService,
val someBodyProvider: (String) -> SomeBody
) {
fun doGetSomething(someValue: String) {
val response = service.getSomething(someBodyProvider(someValue))
}
}
And mock it in your tests:
val someValue = "foo"
val sampleService: SampleService = mock()
val someBody: SomeBody = mock()
val someBodyProvider: (String) -> SomeBody = mock {
on { invoke(someValue) }.thenReturn(someBody)
}
val userRequester = UserRequester(sampleService, someBodyProvider)
userRequester.doGetSomething("foo")
verify(sampleService).getSomething(someBody)
verify(someBodyProvider).invoke(someValue)
I used an anonymous function but you might as well make it an interface.

How to mock objects in Kotlin?

I want to test a class that calls an object (static method call in java) but I'm not able to mock this object to avoid real method to be executed.
object Foo {
fun bar() {
//Calls third party sdk here
}
}
I've tried different options like Mockk, How to mock a Kotlin singleton object? and using PowerMock in the same way as in java but with no success.
Code using PowerMockito:
#RunWith(PowerMockRunner::class)
#PrepareForTest(IntentGenerator::class)
class EditProfilePresenterTest {
#Test
fun shouldCallIntentGenerator() {
val intent = mock(Intent::class.java)
PowerMockito.mockStatic(IntentGenerator::class.java)
PowerMockito.`when`(IntentGenerator.newIntent(any())).thenReturn(intent) //newIntent method param is context
presenter.onGoToProfile()
verify(view).startActivity(eq(intent))
}
}
With this code I get
java.lang.IllegalArgumentException: Parameter specified as non-null is null: method com.sample.test.IntentGenerator$Companion.newIntent, parameter context
any() method is from mockito_kotlin. Then If I pass a mocked context to newIntent method, it seems real method is called.
First, that object IntentGenerator looks like a code smell, why would you make it an object? If it's not your code you could easily create a wrapper class
class IntentGeneratorWrapper {
fun newIntent(context: Context) = IntentGenerator.newIntent(context)
}
And use that one in your code, without static dependencies.
That being said, I have 2 solutions. Say you have an object
object IntentGenerator {
fun newIntent(context: Context) = Intent()
}
Solution 1 - Mockk
With Mockk library the syntax is a bit funny compared to Mockito but, hey, it works:
testCompile "io.mockk:mockk:1.7.10"
testCompile "com.nhaarman:mockito-kotlin:1.5.0"
Then in your test you use objectMockk fun with your object as argument and that will return a scope on which you call use, within use body you can mock the object:
#Test
fun testWithMockk() {
val intent: Intent = mock()
whenever(intent.action).thenReturn("meow")
objectMockk(IntentGenerator).use {
every { IntentGenerator.newIntent(any()) } returns intent
Assert.assertEquals("meow", IntentGenerator.newIntent(mock()).action)
}
}
Solution 2 - Mockito + reflection
In your test resources folder create a mockito-extensions folder (e.g. if you're module is "app" -> app/src/test/resources/mockito-extensions) and in it a file named org.mockito.plugins.MockMaker. In the file just write this one line mock-maker-inline. Now you can mock final classes and methods (both IntentGenerator class and newIntent method are final).
Then you need to
Create an instance of IntentGenerator. Mind that IntentGenerator is just a regular java class, I invite you to check it with Kotlin bytecode window in Android Studio
Create a spy object with Mockito on that instance and mock the method
Remove the final modifier from INSTANCE field. When you declare an object in Kotlin what is happening is that a class (IntentGenerator in this case) is created with a private constructor and a static INSTANCE method. That is, a singleton.
Replace IntentGenerator.INSTANCE value with your own mocked instance.
The full method would look like this:
#Test
fun testWithReflection() {
val intent: Intent = mock()
whenever(intent.action).thenReturn("meow")
// instantiate IntentGenerator
val constructor = IntentGenerator::class.java.declaredConstructors[0]
constructor.isAccessible = true
val intentGeneratorInstance = constructor.newInstance() as IntentGenerator
// mock the the method
val mockedInstance = spy(intentGeneratorInstance)
doAnswer { intent }.`when`(mockedInstance).newIntent(any())
// remove the final modifier from INSTANCE field
val instanceField = IntentGenerator::class.java.getDeclaredField("INSTANCE")
val modifiersField = Field::class.java.getDeclaredField("modifiers")
modifiersField.isAccessible = true
modifiersField.setInt(instanceField, instanceField.modifiers and Modifier.FINAL.inv())
// set your own mocked IntentGenerator instance to the static INSTANCE field
instanceField.isAccessible = true
instanceField.set(null, mockedInstance)
// and BAM, now IntentGenerator.newIntent() is mocked
Assert.assertEquals("meow", IntentGenerator.newIntent(mock()).action)
}
The problem is that after you mocked the object, the mocked instance will stay there and other tests might be affected. A made a sample on how to confine the mocking into a scope here
Why PowerMock is not working
You're getting
Parameter specified as non-null is null
because IntentGenerator is not being mocked, therefore the method newIntent that is being called is the actual one and in Kotlin a method with non-null arguments will invoke kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull at the beginning of your method. You can check it with the bytecode viewer in Android Studio. If you changed your code to
PowerMockito.mockStatic(IntentGenerator::class.java)
PowerMockito.doAnswer { intent }.`when`(IntentGenerator).newIntent(any())
You would get another error
org.mockito.exceptions.misusing.NotAMockException: Argument passed to
when() is not a mock!
If the object was mocked, the newInstance method called would not be the one from the actual class and therefore a null could be passed as argument even if in the signature it is non-nullable
Check your any(), it returns null, and this is the cause of your exception.
To debug, replace any() with any().also(::println) and see the command line output.
If any().also(::println) fails please use any<MockContext>().also(::println).
I've just read the implementation of the mock framework. any has a type parameter which has implicitly inferred. From your error message, I guess the type of newIntent's parameter is Context, which is an abstract class, so of course you can't create an instance of it.
If my guess is true, replace any() with any<Activity>() will probably solve this.
And whether my guess is true or not, please read the StackOverflow help center and "git gud scrub" about how to ask programming questions. The information you've provided is extremely unhelpful for solving your question.
This blog post clearly demonstrates how to do this using mockito. I highly recommend this approach. It's really simple.
Basically, in your src/test/resources folder create a folder: mockito-extensions. Add a file in that directory called org.mockito.plugins.MockMaker, and inside that file have the following text:
mock-maker-inline
You can now mock final kotlin classes and methods.

Categories

Resources