I am trying to test ViewModel to make sure livedata gets updated correctly. However when using ArgumentMatchers.any() it fails with IllegalStateException saying:
ArgumentMatchers.any(mViewModel.CountSubscriber::class.java) must not
be null
#Test
fun emitValueIfCountIs7() {
doAnswer { invocation: InvocationOnMock ->
val subscriber: mViewModel.CountSubscriber = invocation.getArgument(0)
subscriber.onNext(7)
null
}.`when`(countUseCase).execute(
ArgumentMatchers.any(mViewModel.CountSubscriber::class.java),
ArgumentMatchers.any(Parameters::class.java)
)
// When
mViewModel.getCount()
// Verify
assert(mViewModel.countResponse.value != null)
}
I am using Kotlin and have the following dependencies:
testImplementation 'junit:junit:4.12'
testImplementation "org.mockito:mockito-inline:2.23.4"
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0"
Here are my imports:
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import com.nhaarman.mockitokotlin2.doAnswer
import io.reactivex.Observable
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.ArgumentMatchers.any
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
import org.mockito.invocation.InvocationOnMock
Strange thing is that it used to work before, and I don't know what has happened that could affect this.
Getting matchers to work with Kotlin can be a problem.
If you have a method written in kotlin that does not take a nullable parameter, then we cannot match with it using Mockito.any().
This is because it can return void and this is not assignable to a non-nullable parameter.
If the method being matched is written in Java, then I think that it will work as all Java objects are implicitly nullable.
One possible solution would be to use a library like mockito-kotlin
But you can solve this issue easily with a few lines of code yourself.
If you need typed any(type: Class)
private fun <T> any(type: Class<T>): T = Mockito.any<T>(type)
OR
You can use this matcher instead of Matchers.any() :
object MockitoHelper {
fun <T> anyObject(): T {
Mockito.any<T>()
return uninitialized()
}
#Suppress("UNCHECKED_CAST")
fun <T> uninitialized(): T = null as T
}
and use MockitoHelper.anyObject() instead of any() in your kotlin tests.
You can find more information in this post: Using Mockito with Kotlin
There is a discussion about possible solutions in this post :
Is it possible to use Mockito in Kotlin?
The correct solution is mentioned in the comment section of the question by #Ana Koridze. Yes, if you are using Koltin + mockitoKotlin. Make sure you are using the following import:
1 - Using the Mockito-kotlin:
import org.mockito.kotlin.any from Mockito-kotlin
instead of
import org.mockito.Mockito.any
testImplementation "org.mockito.kotlin:mockito-kotlin:$mockito_kotlin_version"
2 - Or if you are using older mockito kotlin version original created by nhaarman before the intergation
import com.nhaarman.mockitokotlin2.any from nhaaram's Mockito-kotlin instead of import org.mockito.Mockito.any
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:$mockito_kotlin2_version"
BTW, if you are using Android Studio or IntelliJ IDEA. the any() from mockitokotlin library should be italic by default style/color scheme.
Notice the any() at the end of line. This is from mockitokotlin
And here is the any() from mockito
Thanks #Sattar for the recommended edit.
mockito-kotlin has added support for nullable args with anyOrNull()
`when`(myMock.doesThis(any(), anyOrNull())).thenReturn(someObject)
use Mockito-kotlin
testImplementation "org.mockito.kotlin:mockito-kotlin:$mockito_kotlin_version"
this will use any that works for kotlin as this is a wrapper lib for Mockito but for kotlin
This is what worked for me,
either replaced all generic any()s , with a specific anyTYPE(), i.e anyInt(), anyList() etc from core lib org.mockito:mockito-core and it fixes the (nullability)issue, it seems the specific definitions with type can handle null. this option does not require you to import any extra lib
or
if you really need to use the generic type any() , add this official Mckito extension lib https://github.com/mockito/mockito-kotlin and make sure you use the any() from this lib (by making sure your imports has this in it import org.mockito.kotlin.any)
I suggest to start using MockK lib https://github.com/mockk/mockk instead of Mockito as it is a Mock library for Kotlin = MockK)
however, if you feel lazy to switch right now or maybe dealing with legacy tests (as in my case :), this should fix your issue too.
For me all solutions above were not enough - in addition to that I had to mark the called method as an 'open' method.
According to this:
https://github.com/mockito/mockito-kotlin/wiki/Parameter-specified-as-non-null-is-null
The method is final and Mockito couldn't mock it so I had to add 'open'.
I wrote a simple wrapper function around Mockito's any() and got rid of the warning.
private fun <T> any() : T {
return org.mockito.ArgumentMatchers.any()
}
I am still pretty green at Kotlin though, so I am not sure whether there may be some unwanted side effects. I put this answer out there in case it helps anyone or someone gives me feedback.
Related
I'm struggling with mine SharedFlow inside of the repo. So, in the real implementation I've got a singleton class that subscribes to different flows, makes some operations and then does shareIn() to all other places in the app as a single source of truth (service, VMs and so on). So it's a huge long chain of calls and setups.
When I'm trying to get a first emitted value, I'm either getting a non-completed coroutine issue (all other tests work fine with my test dispatcher, so I don't think that it's a dispatcher issue), or when I'm trying to use sharedFlow.toList(someMutableList) as I found in other examples, I'm getting an empty list of results. I cannot emit to the flow from my test because 1) I want to test that it works well by itself (that it combines other flows, makes some api calls etc) 2) and it's obviously a read-only class field and I want it to be so.
Here I created a really simple version of my repo and test class and a basic vision what I want to achieve.
import junit.framework.Assert.assertEquals
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.runTest
import org.junit.Test
class Repo(dispatcher: CoroutineDispatcher) {
val flow1 = MutableStateFlow(0)
val flow2 = MutableStateFlow(1)
val sharedFlow = flow1.flatMapLatest { zero ->
flow2.map { one -> zero to one }
}.transformLatest { (zero, one) ->
emit(zero + one)
}.shareIn(CoroutineScope(dispatcher), SharingStarted.Eagerly, 1)
}
class TestClass {
#Test
fun `checking the right state for the shared flow in repo`() = runTest {
val repo = Repo(testDispatcher)
launch {
val state = repo.sharedFlow.firstOrNull()
assertEquals(state, 1) // 0 + 1
}.join()
}
}
What am I doing wrong here? Thanks in advance for any help!
Okay, so I've figured it out. The funniest and most awkward thing is that my example works perfectly, but the real implementation didn't.
The issue was with #RelaxedMockK that swallowed the exception that one of my flows was not mocked properly. Because of that the coroutine wasn't completed and caused a time out exception instead of a real-swallowed one (that's why I thought that maybe I had an issue with a dispatcher). So, if you use the test dispatcher, provide it to your repo, mock everything properly without relying on relaxed mockks when it's not a unit-result function and wait until the flow receives the first data, then everything should work well.
So according to android documentation, defaultBehaviour is deprecated and AttachedBehaviour should be used instead.
However:
does not "exist" in android. I always receive the Annotation type expected error.
My import is:
import androidx.coordinatorlayout.widget.CoordinatorLayout;
Am I using the wrong import?
AttachedBehavior is an interface, not an annotation.
Therefore your CustomLinearLayout must implement AttachedBehavior and override the getBehavior() method to return an instance of your MoveUpwardBehavior class.
I am trying to find the main thread for subscribeOn in Rx3
Single.just(getHeavyData())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Data>() {
#Override
public void accept(Data d) throws Throwable {
setAdapters(d);
}
});
AndroidSchedulers.mainThread() - is not compatible with the brand new RX3
Gradle import: implementation "io.reactivex.rxjava3:rxjava:3.0.0-RC3"
How can we find the main thread in order to do changes to the UI?
AndroidSchedulers.mainThread() is not part of Rx Java 1,2 or 3. Its the part of RxAndroid Library. Add RxAndroid dependency to your project and you will get this method.
RxAndroid still uses RxJava2. Until there is an update from the creators of the library this problem remains.
The new package structure has been released with 3.0.0-RC2 and there is a support library so that v2 and v3 can talk to each other without hidden or overt compilation/runtime problems from before.
This also means that module override tricks no longer work so you have to bridge AndroidSchedulers manually or convert from v2 sources used in Retrofit until these (and many other) libraries start supporting v3.
Please refer this and this
This is an issue with imports.
I had a similar problem before realizing that I need to reference RxJava3.
Use these imports for Schedulers and Disposable Single Observers
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.observers.DisposableSingleObserver;
import io.reactivex.rxjava3.schedulers.Schedulers;
Also, if you have a service with a method that returns a Single use
import io.reactivex.rxjava3.core.Single;
Had a similar problem and the compatibility issue was from an import of the Single. Even though I was using RxJava3 library and RxAndroid3, I was still using:
import io.reactivex.Single
But was resolved when pointing the Single to:
import io.reactivex.rxjava3.core.Single
Check your import, maybe you are importing something like:
implementation 'io.reactivex.**rxjava2**:rxandroid:x.y.z'
but you should be importing something like:
implementation 'io.reactivex.**rxjava3**:rxandroid:3.0.0' (check the last version)
that solved my problem
I'm new to Kotlin Android so in writing tests these asserts unexpectedly pass:
import org.junit.Test
assert("x".equals("y"))
assert("x" == "y")
but this does fail:
import junit.framework.Assert.assertEquals
assertEquals("x", "y")
So I reviewed string structural comparisons.
And then found that this also passes:
assert(false)
Looks like org.junit.Test comes by default in a new project via:
testImplementation 'junit:junit:4.12'
So now I'm wondering what's the correct testImplementation package to sync with. Am I using the wrong one?
From the doc for assert (https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/assert.html):
Throws an AssertionError if the value is false and runtime assertions
have been enabled on the JVM using the -ea JVM option.
What you should do is to use Assert.assertTrue or Assert.assertEquals from org.junit instead.
You're using kotlin.assert which are only enabled when platform assertions are enabled. The checks that you say should cause errors simply are never executed.
Either run it with the -ea (enable assertsions) JVM parameter or use assertEquals which is the usual test framework name since assert is a keyword in java.
Looking for inside documentation, assertEquals hasn't two String parameters to compare. It only receive generic Objects:
assertEquals(java.lang.Object expected, java.lang.Object actual) =>
Asserts that two objects are equal.
Strings should be compared using .equals(String) method due using Objects have differents address memory access, so these are differents despite of they have same content.
:: Edit ::
To have the sames Objects type of Strings you should use .clone() method. It returns same content and same references from original object.
Is there any way I can Mock Static Function in Android using any Mocking Framework.
Mockito can mock classes but is insuffiecient to mock Static functions.
Any help will be highly appreciated.
Thanks in Advance
Mocking works by using the concepts of Object Orientation, Inheritance etc....
Basically by overriding certain methods & behaviour in objects / instances that look like real objects, because they are subclasses of these real objects.
In other words, the mocking part comes in overriding methods on instances.
It is not possible to override a static method (afaik).
Therefore mocking of static calls is not easy (if even possible).
EDIT - I was wrong...
As it turns out, I was wrong in my above statement that it is not possible.
I should have searched this site for duplicate questions. See below for some links to frameworks that claim to do this for you in some cases. Since they work with bytecode, I'm not sure they will work properly on Android (ymmv).
Mocking Static Methods
How can I easily mock out a static method in Java (jUnit4)
(thanks to Rohit for forcing me to reassess my beliefs)
Please try this instead: https://bintray.com/linkedin/maven/dexmaker-mockito-inline-extended
It helps me successfully mock the static method in the Android Instrumented Tests, but note that this feature requires running on a device with at least Android P.
Here is what I did:
Replace the androidTestImplementation 'org.mockito:mockito-android:2.28.0' with androidTestImplementation 'com.linkedin.dexmaker:dexmaker-mockito-inline-extended:2.28.0'
Then mock the static method like this:
static class StaticTrojan {
static String staticOpen() { return "horse"; }
}
#Test
public void testStubbingStaticMethod() {
MockitoSession session = mockitoSession().spyStatic(StaticTrojan.class).startMocking();
try {
when(StaticTrojan.staticOpen()).thenReturn("soldiers");
assertEquals("soldiers", StaticTrojan.staticOpen());
} finally {
session.finishMocking();
}
// Once the session is finished, all stubbings are reset
assertEquals("horse", StaticTrojan.staticOpen());
}
If you use Kotlin, then to mocking static functions you can connect the mockk library project:
androidTestImplementation "io.mockk:mockk-android:1.12.0"
Then you need to add a AndroidManifest.xml to the androidTest directory if your tests are located in the application module.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="you.application.package">
<application
android:debuggable="true"
android:extractNativeLibs="true" />
</manifest>
Then you can mocking static functions using the following code:
import io.mockk.every
import io.mockk.mockkStatic
import io.mockk.unmockkStatic
import org.junit.Assert.assertEquals
import org.junit.Test
class TestMockingStaticFunction {
object StaticTrojan {
#JvmStatic
fun staticOpen(): String {
return "horse"
}
}
#Test
fun testMockingStaticFunction() {
assertEquals("horse", StaticTrojan.staticOpen())
mockkStatic(StaticTrojan::staticOpen)
val mockScope = every { StaticTrojan.staticOpen() } returns "solders"
assertEquals("solders", StaticTrojan.staticOpen())
unmockkStatic(StaticTrojan::staticOpen)
assertEquals("horse", StaticTrojan.staticOpen())
}
}
Library API allows you to comfortably mock the Kotlin objects, in the example above the object is used only to create a static function using #JvmStatic annotation.
Attention! This approach uses JVMTI available in Android P starting from the API level 28. Your application can be written using a smaller API, but the tests you must run only on Android P devices or newer.