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 (eg 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
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.
Seehttps://kotlinlang.org/docs/properties.html#getters-and-setters for details on property getters.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.