I've been using Mockito, Mockk and PowerMock to mock static methods, to not avail. I'm not sure if my understanding is correct, but I was expecting that, when I mock a (static or instance) method, the actual method implementation is bypassed and the provided value returned.
This seems to work great with instance methods, but for some reason I can't get it to work with static methods. It seems it actually goes into the mocked method's implementation (which forces me to go into the rabbithole of mocking the entire call chain).
I want to mock an isOnline(context: Context) method, which returns boolean by calling a series of Android calls to check for connectivity. I want to stub this method to return true or false depending on the test scenario, but upon passing it a mocked context, I get an error about casting null to ConneectivityManager (included snippet of implementation below).
Example (using Mockk): The mocking:
context = Mockito.mock(Context::class.java)
mockkStatic(NetworkManager::class)
every { NetworkManager.isOnline(context) } answers { true}
The test:
assertThat(NetworkManager.isOnline(context)).isTrue()
NetworkManager snippet:
fun isOnline(context: Context): Boolean {
val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager` <-- here's where the test fails, says that cannot cast null to
Same happens when using Mockito:
mockStatic(NetworkManager::class.java)
.use { mocked ->
mocked.`when`<Boolean> { NetworkManager.isOnline(context) }.thenReturn(true)
assertThat(NetworkManager.isOnline(context)).isTrue()
}
PowerMock/EasyMock:
context = mock(Context::class.java);
createMock(NetworkManager::class.java);
expect(NetworkManager.isOnline(context)).andReturn(true);
assertThat(NetworkManager.isOnline(context)).isTrue();
Is there any way around it? I see documentation about mocking static methods, but I haven't managed to make it work.
Thank you!
Try mockObject:
mockkObject(NetworkClassManager.Companion)
According to this answer: https://github.com/mockk/mockk/issues/136#issuecomment-419879755
The best solution is to use mockk. But if you are stuck with mockito the @JvmStatic on the method that you need to mock. Like this ->
object MyObject {
@JvmStatic fun getValue() = 1
}
Then use it in test like this ->
@Test
fun x() {
val mockedMyObject = Mockito.mockStatic(MyObject::class.java)
mockedMyObject.`when`<Int>(MyObject::getValue).thenReturn(4)
println(MyObject.getValue())
}
The Kotlin object ends up being this ->
public final class MyObject {
@NotNull
public static final MyObject INSTANCE;
public final int getValue() {
return 1;
}
private MyObject() {
}
static {
MyObject var0 = new MyObject();
INSTANCE = var0;
}
}
So when you call MyObject.getValue() you are actually accessing the static INSTANCE field before calling the getValue() function. Mockito isn’t super great at mocking static stuff like that. This is mostly because the mockStatic support in mockito is java based, and not optimized for kotlin. mockk is designed from the ground up for pure kotlin code, and thus doesn’t come with some of the baggage that mockito does. What I’d say is there’s no reason the two can’t coexist in the same project. If mockk is needed for one test just for this reason, then use it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With