Closed karlkar closed 4 years ago
@karlkar Are you using Mockito-inline or AllOpen plugin? If not you won't be able to mock it.
I'm not using AllOpen plugin and don't really want to go this way. In case of Mockito-inline - I've added mock-maker-inline file. Isn't that enough?
Should be enough, but sometimes people setup the file wrongly.
Maybe try the relatively new testImplementation "org.mockito:mockito-inline:$mockito_version"
gradle dependency. This can be used instead of using the inline-mock-maker file.
I've added
testImplementation "org.mockito:mockito-inline:2.28.2"
didn't help - doesn't matter if I have MockMaker file created or not.
@karlkar Just realized these are instrumentation tests that are executed as such. Inline-mock-maker won't work here.
Inline-mock-maker will only work for tests on the JVM as it targets java bytecode. Instrumentation tests are meant to be executed in the Android environment using dalvik bytecode hence the issue.
Only solution to this is linkedins dexmaker I believe. But haven't tried it.
No, those are unit tests. And in fact I've added a third test case to my set:
class FinalClass {
val finalField: String = "something"
}
@Test
fun test() {
val concrete = FinalClass()
val mocky = mock(FinalClass::class.java)
given(mocky.finalField).willReturn("not anymore");
val res = mocky.finalField
assert(res != concrete.finalField)
}
and it works fine ^^
Ohh okay. Are you using Robolectric?
What if you move the stubbing inside the test as in the above instead of using on { core } doReturn crashlyticsCore
?
Ok, to be very precise - currently I have MockMaker file created in proper directory (checked 100 times). in build.gradle I've added
testImplementation 'junit:junit:4.12'
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
testImplementation 'org.mockito:mockito-inline:2.28.2'
so MockMaker file should not be necessary, but let it stay there. Now if I use such code:
import com.crashlytics.android.Crashlytics
import com.crashlytics.android.core.CrashlyticsCore
import com.nhaarman.mockitokotlin2.doReturn
import com.nhaarman.mockitokotlin2.given
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.verify
import org.junit.Test
import java.io.IOException
class CrashlyticsCrashReportingServiceTest {
private val crashlyticsCore: CrashlyticsCore = mock()
private val crashlytics: Crashlytics = mock {
on { core } doReturn crashlyticsCore
}
private val tested: CrashlyticsCrashReportingService =
CrashlyticsCrashReportingService(crashlytics)
@Test
fun `should log exception to crashlytics when requested`() {
// given
val exception: IOException = mock()
given(crashlytics.core).willReturn(crashlyticsCore)
// when
tested.reportCrash(exception)
// then
verify(crashlyticsCore).logException(exception)
}
@Test
fun `given message is provided should log message when logging exception when requested`() {
// given
val message = "Message"
val exception: IOException = mock()
given(crashlytics.core).willReturn(crashlyticsCore)
// when
tested.reportCrash(exception, message)
// then
verify(crashlyticsCore).log(message)
verify(crashlyticsCore).logException(exception)
}
class FinalClass {
val finalField: String = "something"
}
@Test
fun test() {
val concrete = FinalClass()
val mocky: FinalClass = mock()
given(mocky.finalField).willReturn("not anymore")
val res = mocky.finalField
assert(res != concrete.finalField)
}
}
all 3 tests are failing.
But when I switch to using Mockito:
import com.crashlytics.android.Crashlytics
import com.crashlytics.android.core.CrashlyticsCore
import org.junit.Assert.assertThat
import org.junit.Ignore
import org.junit.Test
import org.mockito.BDDMockito.given
import org.mockito.Mockito
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import java.io.IOException
class CrashlyticsCrashReportingServiceTest {
private val crashlyticsCore: CrashlyticsCore = mock(CrashlyticsCore::class.java)
private val crashlytics: Crashlytics = mock(Crashlytics::class.java)
private val tested: CrashlyticsCrashReportingService =
CrashlyticsCrashReportingService(crashlytics)
@Test
fun `should log exception to crashlytics when requested`() {
// given
val exception: IOException = mock(IOException::class.java)
Mockito.`when`(crashlytics.core).thenReturn(crashlyticsCore)
// when
tested.reportCrash(exception)
// then
verify(crashlyticsCore).logException(exception)
}
@Test
fun `given message is provided should log message when logging exception when requested`() {
// given
val message = "Message"
val exception: IOException = mock(IOException::class.java)
Mockito.`when`(crashlytics.core).thenReturn(crashlyticsCore)
// when
tested.reportCrash(exception, message)
// then
verify(crashlyticsCore).log(message)
verify(crashlyticsCore).logException(exception)
}
class FinalClass {
val finalField: String = "something"
}
@Test
fun test() {
val concrete = FinalClass()
val mocky = mock(FinalClass::class.java)
given(mocky.finalField).willReturn("not anymore");
val res = mocky.finalField
assert(res != concrete.finalField)
}
}
the third test passes.
I am not using Robolectric.
When I've removed the on {} part from the initailziers 3rd test has passed also for the first case. The rest stays red.
The problem comes from this I believe:
- inside when() you don't call method on mock but on some other object.
Try using the following inside the test (not sure it'll work though):
doReturn(crashlyticsCore).whenever(crashlytics.core)
It didn't help :( I went further - I've created 2 classes to simulate Crashlytics. One in Java (just like Crashlytics) and one in kotlin:
public class PseudoCrashlytics {
public final PseudoCrashlyticsCore core;
public PseudoCrashlytics() {
core = new PseudoCrashlyticsCore();
}
public class PseudoCrashlyticsCore {
public void log(String message) {}
public void logException(Throwable ex) {}
}
}
class PseudoCrashlytics2 {
val core: PseudoCrashlyticsCore = PseudoCrashlyticsCore()
inner class PseudoCrashlyticsCore {
fun log(m: String){}
fun logException(m: Throwable){}
}
}
In case of kotlin's version all tests are green. In case of Java version - tests are red (besides the last one with the FinalClass of course).
Ahh yes java. Forgot about that.
Add a getter for the final field to the java version and you should get the same output as the kotlin one (you could even add final
modifier to the getter).
Mocking operates on methods and interfaces, not fields
That's a 3rd party class :/ Ok, I'll have to create some kind of a wrapper for it. Thanks for your help!
Hello, I'm trying to use mockito-kotlin 2.2.0 in order to mock a final field in a final class of Crashlytics.
I have added src/test/resources/mockito-extensions.org.mockito.plugins.MockMaker file with "mock-maker-inline" text inside of it. However I'm still getting
How can I fix that?