I have a simple class with a private field.
class EmployeeData {
private var employeeAge: Int = 0
fun getAge(): Int {
return 1 + employeeAge
}
}
I am trying to test this private employeeAge with the following from official docs
#Test
fun testPrivateAge() {
val mock = spyk(EmployeeData())
every {
mock getProperty "employeeAge"
} propertyType Int::class answers { fieldValue + 6 }
every {
mock setProperty "employeeAge" value any<Int>()
} propertyType Int::class answers { fieldValue += value }
every { mock getProperty "employeeAge" } returns 33
every { mock setProperty "employeeAge" value less(5) } just Runs
assertEquals(10,mock.getAge())
}
I am receiving such exception from MockK
io.mockk.MockKException: Missing calls inside every { ... } block.
at io.mockk.impl.recording.states.StubbingState.checkMissingCalls(StubbingState.kt:14)
at io.mockk.impl.recording.states.StubbingState.recordingDone(StubbingState.kt:8)
Any clue on what's I am doing wrong? Official docs suggest using such technique against private properties but for me it doesn't work and I'm using latest on this moment version of MockK which is v1.10.0.
Though for private methods it is working like a charm. I am able to test the private method in this logic.
This is a problem with some Kotlin optimisations. According to MockK author "Brief explanation. It is nearly impossible to mock private properties as they don't have getter methods attached. This is kind of Kotlin optimisation and solution is major change."
More info can be found on these 2 Github issues:
https://github.com/mockk/mockk/issues/263
https://github.com/mockk/mockk/issues/104
Related
I have written test cases for my view model. Which when I run individually or when I run the Test class. They get executed successfully. But when I run the complete androidTest package, I get this Exception
io.mockk.MockKException
Here is the code that runs successfully in isolation.
#RunWith(AndroidJUnit4::class)
class MyViewModelTest{
#Test
fun test_one(){
getInstrumentation().runOnMainSync(Runnable {
val context = ApplicationProvider.getApplicationContext<Context>()
mockkStatic(MyManager::class)
val myInterface = mockk<MyInterface>()
every { MyManager.getCommunicator() } returns myInterface
every { myInterface.context } returns context
every { myInterface.getLongFromGTM(any()) } returns 0
val viewModel = MyViewModel(context as Application)
viewModel.model = MyDataModel()
viewModel.model.isRepeatEligible = true
val res = viewModel.isRepeatEligible()
Truth.assertThat(res).isTrue()
})
}
}
This is the error I am getting while running entire androidTest package:
Here are the detailed used classes
1 .) MyManager.java
public class MyManager {
private static MyInterface myCommunicator;
public static MyInterface getCommunicator() {
if (myCommunicator == null) {
synchronized (MyManager.class) {
if (myCommunicator == null) {
Class<?> cls = Class.forName("mypackage.communicator.MyCommunicator");
myCommunicator = (MyInterface) cls.newInstance();
}
}
}
return myCommunicator;
}
}
2.) MyViewModel.kt
class MyViewModel(application: Application) : BaseViewModel(application) {
var model = MyDataModel()
private val timerDelay: Long by lazy {
myCommunicator.getLongFromGTM("key_p2m_timer_delay")
}
val timerDuration: Long by lazy {
myCommunicator.getLongFromGTM("key_p2m_timer_duration")
}
fun isRepeatEligible(): Boolean {
model.apply {
return isRepeatEligible && !isLinkBased && !isAlreadyPresent
}
}
Mocking something with MockK is not contrained to just one function. Specifically, when you mock an object with mockkStatic, the object will from then on be a mock until it is unmocked using unmockkStatic or unmockkAll.
In your case, I guess the problem arises due to the static mocking of MyManager that lets subsequent tests fail, because they do not expect the object to be mocked.
This could be solved with an "after" function (e.g. using JUnit4, a function annotated with #After) that calls unmockAll.
Alternatively, if you want to make sure that the object is only mocked locally, you can use a variant of mockkStatic that accepts a block that is the only place where the object is mocked like this:
mockkStatic(MyManager::class) {
// inside this block, MyManager is mocked
}
// MyManager is automatically unmocked after the block
Update
As mentioned in your comment, you do not call MyManager.getCommunicator() directly in MyViewModel, but via an extension property
val myCommunicator : MyInterface = MyManager.getCommunicator()
This may cause your test setup to be still in place after your test, even when you unmock MyManager, because the property myCommunicator will keep its value - the mocked interface.
This can be solved by changing your property to not be initialized with the value of MyManager.getCommunicator(), but instead you should define a getter that calls MyManager.getCommunicator():
val myCommunicator: MyInterface get() = MyManager.getCommunicator()
This way, you do always get the current value of MyManager.getCommunicator() and not the value that was set once on initialization.
See https://kotlinlang.org/docs/properties.html#getters-and-setters for details on property getters.
I am facing the same issue as asked in the below question. please help me out.
Mock a "global" property in Kotlin
I tried solution provided in above question but nothing is working. and I am asking the same question because I am not able to post any comment on the previous question.
I am trying to write test case for below class
class CustomLogger constructor(val ctx: Context, embEnabled: Boolean = false) : Logger {
private val loggers = arrayListOf<Logger>()
fun get() = loggers
init {
if (embEnabled)
loggers.add(Emb(ctx))
if (BuildConfig.DEBUG)
loggers.add(DebugLogger(ctx))
}
override fun logError(t: Throwable, msg: String?) {
loggers.forEach { logger ->
logger.logError(t, msg)
}
}
}
enter code here
Here I am trying to mock get() or init{}
that was on dam question but i got you
note this can only be used in unittest as mockito static mock is not support on Android JVM
testImplementation "org.mockito:mockito-inline:4.8.1" you gonna need
this so added
Update you need to call this i forgot to add it sorry in your test case before call the method
Mockito.mockStatic(Class.forName("com.udacity.project4.locationreminders.RemindersActivityKt"))
fun getMockForMethod(clazz: Class<*>, methodName: String, methodResponse: Any) {
val method: Method = clazz.getMethod(methodName)
Mockito.`when`(method.invoke(null)).thenReturn(methodResponse)
}
now i created the method to handle no argument methods you can modifiy it as you see fit just pass the class using it name
getMockForMethod(Class.forName("com.udacity.project4.locationreminders.RemindersActivityKt"),
"doSomething","New Response")
Assert.assertEquals("New Response", doSomething())
works like a charm Enjoy 😁
i have updated the above code for anyone to use with static members in kotlin
your updates makes this easy to do now it is a class that you can mock entirly and easliy mock any methods
val loggerMock= Mockito.mock(Logger::class.java)
Mockito.`when`(loggerMock.loggers).thenReturn(new array of loggers)
Having issues with the following conversion from Mockito to Mockk.
So I have this
#Mock
private lateinit var loginLiveDataObserver: Observer<LoginResult>
val inOrder = inOrder(loginLiveDataObserver)
inOrder.verify(loginLiveDataObserver).onChanged(enableLoading)
inOrder.verify(loginLiveDataObserver).onChanged(loginResults)
inOrder.verify(loginLiveDataObserver).onChanged(disableLoading)
Try turning it into this
private val loginLiveDataObserver = mockk<Observer<LoginResult>>()
verifyOrder {
loginLiveDataObserver.onChanged(enableLoading)
loginLiveDataObserver.onChanged(loginResults)
loginLiveDataObserver.onChanged(disableLoading)
}
Gives me the following error
io.mockk.MockKException: no answer found for: Observer(#4).onChanged(Loading(value=true))
Your exception says it all (and one of the direct answers), you always have to specify the behaviour of your mocks..
in your case:
private val loginLiveDataObserver = mockk<Observer<LoginResult>>()
// Example answer you can use different here
every { loginLiveDataObserver.onChange(any()) } answers nothing
verifyOrder {
loginLiveDataObserver.onChanged(enableLoading)
loginLiveDataObserver.onChanged(loginResults)
loginLiveDataObserver.onChanged(disableLoading)
}
Check the Documentation at this point and look for the every { ... } part in the example
hope this helps.
I'm stuck trying to mock some stuff with mockk:
I have the following setup on gradle
root:
|-- App (just a sample app for the SDK)
|-- SDK (SDK we develop) << apply plugin: 'com.android.library'
|-- SDKimpl.kt
|-- Foo (wrapper around a .jar library) << apply plugin: 'com.android.library'
|-- Foo.kt
So I'm writing an androidTest for the SDK and trying to mock Foo.kt.
There's nothing unusual about Foo class, just direct class Foo(private val someParams) {
So using androidTestImplementation "io.mockk:mockk-android:1.8.13" the mock goes:
val mock: Foo = mockk()
// val mock: Foo = mockkClass(Foo::class) // also tried this
every { mock.getData() } returns listOf("1", "2", "3")
I'm always getting the following crash:
io.mockk.MockKException: Missing calls inside every { ... } block.
at io.mockk.impl.recording.states.StubbingState.checkMissingCalls(StubbingState.kt:14)
at io.mockk.impl.recording.states.StubbingState.recordingDone(StubbingState.kt:8)
at io.mockk.impl.recording.CommonCallRecorder.done(CommonCallRecorder.kt:42)
Also tried just to gather information:
running inside JVM test folder. It gets mocked without issues, but I can't run my test as JVM
running androidTest inside Foo module. Got the same crash
using mockkClass(Foo::class). Got some crash
using annotation #MockK and MockKAnnotations.init(this). Got some crash.
added Log.d before every { line and inside getData() method and it seems the actual real method from the class is getting called during the mock setup. That seems super weird to me.
Any idea what's going wrong here?
edit:
as requested, full code. I'm current working on an isolated project to try to isolate the error, so Foo is just:
class Foo {
fun getData(): String {
Log.d(TAG, "invoked foo.getData()")
return "trolololo"
}
}
and then I have FooTest in androidTest:
class FooTest {
#Test
fun mock_foo() {
val foo = mockk<Foo>()
every { foo.getData() } returns "zero"
assertEquals("zero", foo.getData())
}
}
It seems to be a Mockk opened issue: https://github.com/mockk/mockk/issues/182
2 possible quick fixes (pick one):
Run the Instrumented Tests in an emulator >= Android-P
Set Foo class as open (and the method(s) you want to mock too)
Try to check the official guide and see what is missing.
In my case, I tried to mock an extension in Kotlin but missed the mockkStatic
fun Date.asMyTime() : DateTime = DateTime(this, DateTimeZone.getDefault())
mockkStatic("packageName.FileNameKt") // This is what I was missing
every {
DateTime().asMyTime()
} returns mock(DateTime::class.java)
In my case I forgot to spyk the class I was applying every {...} to. 😳
val presenter = spyk(MyPresenter())
every { view.myFun(any()) } returns Unit
In my case, I've missed
#Before
fun setUp() {
MockKAnnotations.init(this)
}
In my case I tried to mock using mock() function instead mockk() (double k)
Make sure the object is really a mock, not the real object.
For instance:
- Sdk sdk = Sdk()
+ Sdk sdk = mockk()
every { sdk.crypto } returns mockk()
My problem was that I used a java class without getters
public class KeyStorePasswordPair {
public KeyStore keyStore;
public String keyPassword;
public KeyStorePasswordPair(KeyStore keyStore, String keyPassword) {
this.keyStore = keyStore;
this.keyPassword = keyPassword;
}
}
I needed to add getters for the variables to make mockking work:
public class KeyStorePasswordPair {
public KeyStore getKeyStore() {
return keyStore;
}
public String getKeyPassword() {
return keyPassword;
}
private KeyStore keyStore;
private String keyPassword;
public KeyStorePasswordPair(KeyStore keyStore, String keyPassword) {
this.keyStore = keyStore;
this.keyPassword = keyPassword;
}
}
Mockk Missing calls inside every { ... } block
may also be thrown when you have an every block defined on an object that is not a mockk, for example:
You define stub behavior like this
every { foo.getData() } returns DATA
and then try to:
every { DATA.getVersion() } returns VERSION
Where MY_THING and VERSION objects are declared (and instantiated) in the test class.
The error message is not very informative and a bit misleading in that case.
try like this
`when`(mock.getData()).thenReturn(listOf("1", "2", "3"))
Kotlin extension function is great. But how could I perform unit test on them? Especially those that is of Android SDK provided class (e.g. Context, Dialog).
I provide two examples below, and if anyone could share how I could unit test them, or if I need to write them differently if I really want to unit test them.
fun Context.getColorById(colorId: Int): Int {
if (Build.VERSION.SDK_INT >= 23)
return ContextCompat.getColor(this, colorId)
else return resources.getColor(colorId)
}
and
fun Dialog.setupErrorDialog(body : String, onOkFunc: () -> Unit = {}): Dialog {
window.requestFeature(Window.FEATURE_NO_TITLE)
this.setContentView(R.layout.dialog_error_layout)
(findViewById(R.id.txt_body) as TextView).text = body
(findViewById(R.id.txt_header) as TextView).text = context.getString(R.string.dialog_title_error)
(findViewById(R.id.txt_okay)).setOnClickListener{
onOkFunc()
dismiss()
}
return this
}
Any suggestion would help. Thanks!
The way I'm testing extension functions on Android classes at the moment is by mocking the Android class. I know, this is not an optimal solution as it mocks the class under test and requires certain knowledge about how the function works (as it is always the case when mocking), but as extension functions are internally implemented as static functions I guess it's acceptable until someone comes up with something better.
As an example consider the JsonArray class. We've defined an extension function for receiving the last item's index:
fun JSONArray.lastIndex() = length() - 1
The according test (using the Spek test framework and mockito-kotlin) looks like this.
#RunWith(JUnitPlatform::class)
object JsonExtensionTestSpec : Spek({
given("a JSON array with three entries") {
val jsonArray = mock<JSONArray> {
on { length() } doReturn 3
}
on("getting the index of the last item") {
val lastIndex = jsonArray.lastIndex()
it("should be 2") {
lastIndex shouldBe 2
}
}
}
given("a JSON array with no entries") {
val jsonArray = mock<JSONArray>({
on { length() } doReturn 0
})
on("getting the index of the last item") {
val lastIndex = jsonArray.lastIndex()
it("should be -1") {
lastIndex shouldBe -1
}
}
}
})
The difficulty with your functions is, that they also use Android classes internally. Unfortunately I don't have a solution for this right now.