如何对静态方法进行安卓单元测试和模拟

2022-09-03 07:15:42

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

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

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

下面是一个示例,首先是我要测试的方法:

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

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

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

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

        return userid;
    }
}

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

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

此错误

org.mockito.exceptions.misusing.MissingMethodInvocationException

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

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

更不用说不返回任何东西,所以我不知道要将什么传递到我的getUsername方法中!PowerMockito.mockStatic(HelperUtils.class);

任何帮助将不胜感激。


答案 1

静态方法与任何对象都不相关 - 你的是相同的(但有点令人困惑) - 由于这个原因,你甚至应该得到一个编译器警告。helper.fetchUsernameFromInternet(...)HelperUtils.fetchUsernameFromInternet(...)helper.fetchUsernameFromInternet

更重要的是,而不是模拟静态方法你必须使用:,然后 - 完整的例子在这里:PowerMockito模拟单个静态方法和返回对象Mockito.mock@RunWith(...)@PrepareForTest(...)PowerMockito.mockStatic(...)

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

  • 如果您可以更改,则使该方法非静态,现在您可以使用通常的模拟方法HelperUtilsHelperUtilsMockito.mock

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


答案 2

我使用PowerMockito以这种方式做到了这一点。

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

静态功能:

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

测试用例:

@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("name@email.com"));
    }

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

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

编辑 1:

添加以下导入:

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

希望这对你有所帮助。


推荐