[MockK] How to use TextUtils.isEmpty #JUnit #UnitTest #returnDefaultValues
The code used in this post can be found in the gist below.
Problem
android {
...
testOptions {
unitTests.returnDefaultValues = true
}
}
dependencies {
...
testImplementation 'junit:junit:4.+'
testImplementation "io.mockk:mockk:1.12.1"
}
For more convenient test code writing, with returnDefaultValues set in build.gradle,
@Test
fun isEmpty() {
assertTrue(TextUtils.isEmpty(""))
//assertFalse(TextUtils.isEmpty(""))
}
This unit test fails.
No, obviously "" is not null and "".length is 0..?
Reason to fail
Because TextUtils.isEmpty always returns false
Because of JUnit TestCase class cannot use Android related APIs, we have to Mock it.
Use PowerMockito to Mock the static class.
https://stackoverflow.com/questions/35763289/need-help-to-write-a-unit-test-using-mockito-and-junit4
Why do we need to address this
class Hello {
companion object {
fun greet(name: String?): String {
return if (TextUtils.isEmpty(name)) {
"Hello!!"
} else {
"Hello, $name!"
}
}
}
}
If TextUtils.isEmpty always spits false,
we cannot test if true, "Hello!!" in the example code above.
Solution 1: Fix to not use android.text.TextUtils
ex) android.text.TextUtils → kotlin.text.Strings
class Hello {
companion object {
fun greet(name: String?): String {
return if (name.isNullOrEmpty()) {
"Hello!!"
} else {
"Hello, $name!"
}
}
}
}
If you can modify the code you want to test,
There is a way to not use TextUtils at all so that JUnit is not related to android.
In the example code above, I simply changed to use Strings.isNullorEmpty in Kotlin, to bypass.
Actually, it's a bit strange to use MockK to test Java code.
At this point, it seems to be a way to switch the code to be tested, from Java to Kotlin altogether.
Solution 2: Mock TextUtils
class HelloUnitTest {
@Before
fun setUp() {
mockkStatic(TextUtils::class)
every { TextUtils.isEmpty(any()) } answers { arg<String?>(0).isNullOrEmpty() }
}
}
If you cannot change the code to be tested,
To use Strings.isNullOrEmpty when trying TextUtils.isEmpty
declare every { ... } answers { ... }
@Before or before test
PowerMockito.when(TextUtils.isEmpty(any())).thenAnswer((Answer<Boolean>) invocation -> {
CharSequence str = (CharSequence) invocation.getArguments()[0];
return str == null || str.length() == 0;
});
This is the same function we have been using with PowerMockito.
Solution 3: Create class under src\test\android
ex) src\test\java\android\os\Bundle.java, src\test\java\android\text\TextUtils.kt
Even if it looks bad, it is intuitive
If the project is large, the reusability is quite good.
and there are situations that can only be solved like this.
You can use android.util.Log, android.os.Bundle, Looper, etc like this.
#JUnit #UnitTest #Kotlin #MockK
#android #returnDefaultValues #false
#github #gist