Need help to write a unit test using Mockito and JUnit4

淺唱寂寞╮ 提交于 2019-12-08 23:36:01

问题


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


回答1:


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.




回答2:


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"



回答3:


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.




回答4:


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

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



回答5:


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/




回答6:


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




回答7:


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



回答8:


You should use Robolectric:

testImplementation "org.robolectric:robolectric:3.4.2"

And then

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



回答9:


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



回答10:


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;

    }

}


来源:https://stackoverflow.com/questions/35763289/need-help-to-write-a-unit-test-using-mockito-and-junit4

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!