Android

[MockK] How to use TextUtils.isEmpty #JUnit #UnitTest #returnDefaultValues

rhys 2022. 1. 29. 00:52
반응형

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

create if not exist

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.

 

 

 

I'M SO EXHAUSTED

​#JUnit #UnitTest #Kotlin #MockK
#android #returnDefaultValues #false
#github #gist

 

반응형