java 如何android单元测试和模拟静态方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32074356/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 19:36:30  来源:igfitidea点击:

How to android unit test and mock a static method

javaandroidunit-testingmockitopowermock

提问by James

Hi I really hope you can help me, I feel like I've been pulling my hair out for days.

嗨,我真的希望你能帮助我,我觉得我已经把头发拉了好几天了。

I'm trying to write unit tests for a method A. Method A calls a static method B. I want to mock static method B.

我正在尝试为方法 A 编写单元测试。方法 A 调用静态方法 B。我想模拟静态方法 B。

I know this has been asked before, but I feel Android has matured since then, and there must be a way to do such a simple task without re-writing the methods I want to test.

我知道以前有人问过这个问题,但我觉得从那时起Android已经成熟了,必须有一种方法可以在不重新编写我要测试的方法的情况下完成如此简单的任务。

Here is an example, first the method I want to test:

这是一个例子,首先是我要测试的方法:

public String getUserName(Context context, HelperUtils helper) {
    if(helper == null){
        helper = new HelperUtils();
    }
    int currentUserId = helper.fetchUsernameFromInternet(context);

    if (currentUserId == 1) {
        return "Bob";
    } else {
        return "Unknown";
    }
}

Next the static method I want to mock:

接下来是我要模拟的静态方法:

public class HelperUtils {
    public static int fetchUsernameFromInternet(Context context) {
        int userid = 0;

        Log.i("HelperUtils ", "hello");

        return userid;
    }
}

In other languages this is so easy but I just can't make it work in Android. I've tried Mockito, but it appears static methods aren't supported

在其他语言中,这很容易,但我无法让它在 Android 中运行。我试过 Mockito,但似乎不支持静态方法

HelperUtils helper = Mockito.mock(HelperUtils.class);
Mockito.when(helper.fetchUsernameFromInternet(getContext())).thenReturn(1);

This errors

这个错误

org.mockito.exceptions.misusing.MissingMethodInvocationException

org.mockito.exceptions.misusing.MissingMethodInvocationException

I've tried Powermock but I'm not completely sure this is supported by Android. I managed to get powermock running using androidCompile in my gradle file but I get this error:

我试过 Powermock,但我不完全确定 Android 是否支持它。我设法在我的 gradle 文件中使用 androidCompile 运行 powermock 但我收到此错误:

Error:Execution failed for task ':app:dexDebugAndroidTest'. com.android.ide.common.process.ProcessException:

错误:任务 ':app:dexDebugAndroidTest' 的执行失败。com.android.ide.common.process.ProcessException:

Not to mention PowerMockito.mockStatic(HelperUtils.class);Doesn't return anything, so I don't know what to pass into my getUsername method!

更不用说PowerMockito.mockStatic(HelperUtils.class);不返回任何内容,所以我不知道要传递给我的 getUsername 方法!

Any help would be so very much appreciated.

任何帮助将不胜感激。

回答by iirekm

Static methods aren't related to any object - your helper.fetchUsernameFromInternet(...)is the same (but a bit confusing) as HelperUtils.fetchUsernameFromInternet(...)- you should even get a compiler warning due to this helper.fetchUsernameFromInternet.

静态方法与任何对象都没有关系——你helper.fetchUsernameFromInternet(...)是一样的(但有点混乱)HelperUtils.fetchUsernameFromInternet(...)——你甚至应该因此得到编译器警告helper.fetchUsernameFromInternet

What's more, instead of Mockito.mockto mock static methods you have to use: @RunWith(...), @PrepareForTest(...)and then PowerMockito.mockStatic(...)- complete example is here: PowerMockito mock single static method and return object

更重要的是,不是Mockito.mock模拟静态方法,你必须使用:@RunWith(...)@PrepareForTest(...)然后PowerMockito.mockStatic(...)- 完整的例子在这里:PowerMockito mock single static method and return object

In other words - mocking static methods (and also constructors) is a bit tricky. Better solution is:

换句话说 - 模拟静态方法(以及构造函数)有点棘手。更好的解决办法是:

  • if you can change HelperUtils, make that method non-static and now you can mock HelperUtilswith the usual Mockito.mock

  • if you can't change HelperUtils, create a wrapper class which delegates to the original HelperUtils, but doesn't have staticmethods, and then also use usual Mockito.mock(this idea is sometimes called "don't mock types you don't own")

  • 如果您可以更改HelperUtils,请将该方法设为非静态,现在您可以HelperUtils使用通常的方法进行模拟Mockito.mock

  • 如果你不能改变HelperUtils,创建一个包装类,它委托给原始的HelperUtils,但没有static方法,然后也使用通常的Mockito.mock(这个想法有时被称为“不要模拟你不拥有的类型”)

回答by Hiren Patel

I did this way using PowerMockito.

我使用PowerMockito这样做。

I am using AppUtils.class, it contains multiple static methods and functions.

我正在使用AppUtils.class,它包含多个静态方法和函数。

Static function:

静态功能:

public static boolean isValidEmail(CharSequence target) {
    return target != null && EMAIL_PATTERN.matcher(target).matches();
}

Test case:

测试用例:

@RunWith(PowerMockRunner.class)
@PrepareForTest({AppUtils.class})
public class AppUtilsTest {

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        PowerMockito.mockStatic(AppUtils.class);

        PowerMockito.when(AppUtils.isValidEmail(anyString())).thenCallRealMethod();
    }

    @Test
    public void testValidEmail() {
        assertTrue(AppUtils.isValidEmail("[email protected]"));
    }

    @Test
    public void testInvalidEmail1() {
        assertFalse(AppUtils.isValidEmail("[email protected]"));
    }

    @Test
    public void testInvalidEmail2() {
        assertFalse(AppUtils.isValidEmail("@email.com"));
    }
}

Edit 1:

编辑1:

Add following imports:

添加以下导入:

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

Hope this would help you.

希望这会帮助你。