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

时间:2015-08-18 13:51:19

标签: java android unit-testing mockito powermock

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

我正在尝试为方法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方法的内容!

非常感谢任何帮助。

4 个答案:

答案 0 :(得分:6)

静态方法与任何对象无关 - 您的helper.fetchUsernameFromInternet(...)HelperUtils.fetchUsernameFromInternet(...)相同(但有点令人困惑) - 您甚至应该得到编译器警告{{1 }}

更多,而不是helper.fetchUsernameFromInternet来模拟您必须使用的静态方法:Mockito.mock@RunWith(...)然后@PrepareForTest(...) - 完整示例在此处: PowerMockito mock single static method and return object

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

  • 如果您可以更改PowerMockito.mockStatic(...),请将该方法设为非静态,现在您可以使用通常HelperUtils

  • 模拟HelperUtils
  • 如果您无法更改Mockito.mock,请创建一个包装类,该类包含原始HelperUtils,但不具有HelperUtils方法,并且然后也使用通常的static(这个想法有时被称为"不要嘲笑你不会拥有"

答案 1 :(得分:5)

我是这样使用 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;

希望这会对你有所帮助。

答案 2 :(得分:0)

您可以使用嘲笑的最新版本,即3.4。+,该版本允许进行静态嘲讽

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

答案 3 :(得分:0)

以下是一些解决方案:

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