【问题标题】:How to android unit test and mock a static method如何android单元测试和模拟静态方法
【发布时间】:2015-08-18 13:51:18
【问题描述】:

嗨,我真的希望你能帮助我,我觉得我这几天一直在拔头发。

我正在尝试为方法 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:

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

非常感谢任何帮助。

【问题讨论】:

  • 我没有正确的答案,但我最近绕过了单例和静态类,将它们替换为服务定位器模式。它使它们易于测试,并且每个类都需要几分钟才能转换。
  • 我对其他语言的测试不太了解,但是在Java中,static是测试的敌人。
  • 您可能想看看 PowerMock (github.com/jayway/powermock)。它是一个测试框架,可让您测试困难的案例。
  • PowerMockito 只是提供了 PowerMock Mockito 风格的 API,从技术上讲,它们都做同样的事情

标签: java android unit-testing mockito powermock


【解决方案1】:

静态方法与任何对象都不相关 - 您的 helper.fetchUsernameFromInternet(...)HelperUtils.fetchUsernameFromInternet(...) 相同(但有点令人困惑) - 由于此 helper.fetchUsernameFromInternet,您甚至应该收到编译器警告。

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

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

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

  • 如果您不能更改HelperUtils,请创建一个包装类,它代表原始HelperUtils,但没有static 方法,然后也使用通常的Mockito.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;
    

    希望这会对你有所帮助。

    【讨论】:

    • @FazalHussain,我已经更新了答案。请告诉我。
    • type不是String怎么办?
    • @HarishGyanani 你可以使用any()
    【解决方案3】:

    您可以使用 mockito 最新版本,即 3.4.+,它允许静态模拟

    https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#48

    【讨论】:

      【解决方案4】:

      这里有一些解决方案:

      • 将你的静态方法修改为非静态方法或者用非静态方法包裹你的静态方法,然后在Android Test中直接使用Mockito模拟非静态方法。
      • 如果您不想更改任何原始代码,可以尝试使用dexmaker-mockito-inline-extended 在Android Test 中模拟静态方法和最终方法。我成功地用它模拟了静态方法。检查这个 solution
      • 在单元测试中使用Robolectric,然后在单元测试中使用 PowerMock 模拟静态方法。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-08-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-05-11
        • 1970-01-01
        相关资源
        最近更新 更多