Kadari
Kadari

Reputation: 311

Need help to write a unit test using Mockito and JUnit4

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 ....

Upvotes: 29

Views: 18789

Answers (11)

Oleg Novosad
Oleg Novosad

Reputation: 2421

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;
});

Upvotes: 0

Wayne
Wayne

Reputation: 123

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

Upvotes: 3

Francis Bacon
Francis Bacon

Reputation: 4745

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;

    }

}

Upvotes: 3

Hylke
Hylke

Reputation: 705

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;
    });

Upvotes: 2

mac229
mac229

Reputation: 4849

You should use Robolectric:

testImplementation "org.robolectric:robolectric:3.4.2"

And then

@RunWith(RobolectricTestRunner::class)
class TestClass {
    ...
}

Upvotes: 4

Roger Alien
Roger Alien

Reputation: 3060

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;
    }
}

Upvotes: 2

chotemotelog
chotemotelog

Reputation: 235

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

android{
....
 testOptions {
        unitTests.returnDefaultValues = true
 }
}

Upvotes: 4

Wahib Ul Haq
Wahib Ul Haq

Reputation: 4385

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.

Upvotes: 6

Johnny
Johnny

Reputation: 1964

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.

Upvotes: 41

behelit
behelit

Reputation: 1793

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"

Upvotes: 9

Exception
Exception

Reputation: 2323

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/

Upvotes: 4

Related Questions