29

Need help to write a unit test for the below code using Mockito and JUnit4,

public class MyFragmentPresenterImpl { 
      public Boolean isValid(String value) {
        return !(TextUtils.isEmpty(value));
      }
}

I tried below method: MyFragmentPresenter mMyFragmentPresenter

@Before
public void setup(){
    mMyFragmentPresenter=new MyFragmentPresenterImpl();
}

@Test
public void testEmptyValue() throws Exception {
    String value=null;
    assertFalse(mMyFragmentPresenter.isValid(value));
}

but it returns following exception,

java.lang.RuntimeException: Method isEmpty in android.text.TextUtils not mocked. See http://g.co/androidstudio/not-mocked for details. at android.text.TextUtils.isEmpty(TextUtils.java) at ....

AdrianHHH
  • 13,492
  • 16
  • 50
  • 87
Kadari
  • 311
  • 1
  • 3
  • 5

11 Answers11

41

Because of JUnit TestCase class cannot use Android related APIs, we have to Mock it.
Use PowerMockito to Mock the static class.

Add two lines above your test case class,

@RunWith(PowerMockRunner.class)
@PrepareForTest(TextUtils.class)
public class YourTest
{

}

And the setup code

@Before
public void setup() {
    PowerMockito.mockStatic(TextUtils.class);
    PowerMockito.when(TextUtils.isEmpty(any(CharSequence.class))).thenAnswer(new Answer<Boolean>() {
        @Override
        public Boolean answer(InvocationOnMock invocation) throws Throwable {
            CharSequence a = (CharSequence) invocation.getArguments()[0];
            return !(a != null && a.length() > 0);
        }
    });
}

That implement TextUtils.isEmpty() with our own logic.

Also, add dependencies in app.gradle files.

testCompile "org.powermock:powermock-module-junit4:1.6.2"
testCompile "org.powermock:powermock-module-junit4-rule:1.6.2"
testCompile "org.powermock:powermock-api-mockito:1.6.2"
testCompile "org.powermock:powermock-classloading-xstream:1.6.2"

Thanks Behelit's and Exception's answer.

Johnny
  • 1,824
  • 23
  • 16
  • This works only partly. I had that this does not work when calling TextUtils.isEmpty(null) since that didnt match the when statement. See my answer for a better solution – Hylke Aug 15 '18 at 07:08
9

Use PowerMockito

Add this above your class name, and include any other CUT class names (classes under test)

@RunWith(PowerMockRunner.class)
@PrepareForTest({TextUtils.class})
public class ContactUtilsTest
{

Add this to your @Before

@Before
public void setup(){
    PowerMockito.mockStatic(TextUtils.class);
    mMyFragmentPresenter=new MyFragmentPresenterImpl();
}

This will make PowerMockito return default values for methods within TextUtils

You would also have to add the relevant gradle depedencies

testCompile "org.powermock:powermock-module-junit4:1.6.2"
testCompile "org.powermock:powermock-module-junit4-rule:1.6.2"
testCompile "org.powermock:powermock-api-mockito:1.6.2"
testCompile "org.powermock:powermock-classloading-xstream:1.6.2"
behelit
  • 1,765
  • 2
  • 20
  • 34
6

This is a known issue as mentioned by @Exception. In my case, I also stumbled upon same situation but on advice of senior dev decided to use Strings.isNullOrEmpty() instead of TextUtils.isEmpty(). It turned out to be a nice way to avoid it.

Update: I should better mention it that this utility function Strings.isNullOrEmpty() requires Guava library.

Wahib Ul Haq
  • 4,185
  • 3
  • 44
  • 41
4

This is a known issue, due to a clause in Testing Fundamental of Android which says:

You can use the JUnit TestCase class to do unit testing on a class that does not call Android APIs.

The default behavior is problematic when using classes like Log or TextUtils.

To sum up:

  1. android.jar is mock before, so some Android API return value may not be as expected.
  2. JUnit itself is a single measure for the java code, so try not to use the Android API methods.

Source: http://www.liangfeizc.com/2016/01/28/unit-test-on-android/

Exception
  • 2,273
  • 1
  • 24
  • 42
4

add this line in your gradle file in case of Android Studio.

android{
....
 testOptions {
        unitTests.returnDefaultValues = true
 }
}
chotemotelog
  • 233
  • 1
  • 4
  • 13
  • 6
    Having the default value be true. Will result in textUtils always returning true. But there might be cases where you what to test a false case. Test coverage that checks branching will miss the case that is false. – ant2009 Dec 29 '17 at 18:10
4

You should use Robolectric:

testImplementation "org.robolectric:robolectric:3.4.2"

And then

@RunWith(RobolectricTestRunner::class)
class TestClass {
    ...
}
mac229
  • 4,319
  • 5
  • 18
  • 24
  • While this is mostly true, its not necessarily that you **should** use Robolectric, its merely a **could**, since not all apps can use Robolectric (for example when you use SQLCipher) – Hylke Aug 15 '18 at 07:13
3

I was able to solve this error by running the test class with the following.

@RunWith(RobolectricGradleTestRunner.class)
public class MySimpleTest {
.....a bunch of test cases
}

This wiki page explains in greater detail https://github.com/yahoo/squidb/wiki/Unit-testing-with-model-objects

Reaz Murshed
  • 23,691
  • 13
  • 78
  • 98
Wayne
  • 123
  • 5
3

Solution 1:

I would like to provide a Kotlin and a Java version.

Kotlin version:

import android.text.TextUtils

import org.junit.Before

import org.junit.runner.RunWith

import org.mockito.Matchers.any

import org.powermock.api.mockito.PowerMockito

import org.powermock.core.classloader.annotations.PrepareForTest

import org.powermock.modules.junit4.PowerMockRunner



@RunWith(PowerMockRunner::class)

@PrepareForTest(TextUtils::class)

class UserOwnedDataTest1 {



    @Before

    fun setup() {

        PowerMockito.mockStatic(TextUtils::class.java)

        PowerMockito.`when`(TextUtils.isEmpty(any(CharSequence::class.java))).thenAnswer { invocation ->

            val a = invocation.arguments[0] as? CharSequence

           a?.isEmpty() ?: true

        }

    }

}

Java version:

import android.text.TextUtils;



import org.junit.Before;

import org.junit.runner.RunWith;

import org.mockito.stubbing.Answer;

import org.powermock.api.mockito.PowerMockito;

import org.powermock.core.classloader.annotations.PrepareForTest;

import org.powermock.modules.junit4.PowerMockRunner;



import static org.mockito.Matchers.any;



@RunWith(PowerMockRunner.class)

@PrepareForTest(TextUtils.class)

public final class UserOwnedDataTest2 {



    @Before

    public void setup() {

        PowerMockito.mockStatic(TextUtils.class);

        PowerMockito.when(TextUtils.isEmpty(any(CharSequence.class))).thenAnswer((Answer<Boolean>) invocation -> {

            CharSequence a = (CharSequence) invocation.getArguments()[0];

            return !(a != null && a.length() > 0);

        });

    }

}

Do not forget to add the dependencies:

testCompile "org.powermock:powermock-module-junit4:1.6.2"

testCompile "org.powermock:powermock-module-junit4-rule:1.6.2"

testCompile "org.powermock:powermock-api-mockito:1.6.2"

testCompile "org.powermock:powermock-classloading-xstream:1.6.2"

I remember that we still need another dependency, but not clearly.

Anyway you could fix the missing dependency easily.

Solution 2:

Or you could add the same package and class name with TextUtils

package android.text;



public class TextUtils {

    public static boolean isEmpty( CharSequence str) {

        return str == null || str.length() == 0;

    }

}
Francis Bacon
  • 4,080
  • 1
  • 37
  • 48
2

I replaces everywhere in my project TextUtils.isEmpty(...) with this:

/**
 * Util class to be used instead of Android classes for Junit tests.
 */
public class Utils {

    /**
     * Returns true if the string is null or 0-length.
     * @param str the string to be examined
     * @return true if str is null or zero length
     */
    public static boolean isEmpty(@Nullable CharSequence str) {
        return str == null || str.length() == 0;
    }
}
Roger Alien
  • 3,040
  • 1
  • 36
  • 46
2

As a followup to Johnny's answer, to catch TextUtils.isEmpty(null) calls as well, you could use this piece of code.

PowerMockito.mockStatic(TextUtils.class);
PowerMockito.when(TextUtils.isEmpty(any()))
    .thenAnswer((Answer<Boolean>) invocation -> {
        Object s = invocation.getArguments()[0];
        return s == null || s.length() == 0;
    });
Hylke
  • 598
  • 1
  • 3
  • 17
  • 1
    The above helps but to be absolutely correct you need do and a extra check like (CharSequence)s).length or in kotlin s == null || (s is CharSequence && s.length == 0) – Pushpan Feb 16 '20 at 10:35
  • 1
    I took your idea and completed it like- `PowerMockito.`when`(TextUtils.isEmpty(any())).thenAnswer { invocation -> val str = invocation.arguments[0] return@thenAnswer str == null || (str is String && str.length == 0) }` – Harish Gyanani Jul 17 '20 at 09:51
0

With newer power mock (2.0.9) and mockito (3.9.0) I had. to change execution to this one:

when(TextUtils.isEmpty(any())).thenAnswer((Answer<Boolean>) invocation -> {
    CharSequence a = (CharSequence) invocation.getArguments()[0];
    return a == null || a.length() == 0;
});
Oleg Novosad
  • 2,261
  • 1
  • 27
  • 28