Android 使用 Retrofit 和 Mockito 进行单元测试

Android Unit Test with Retrofit and Mockito

我从 activity 代码中分离了改造 api 调用方法,我想对这些方法进行单元测试,一个例子: 接口:

public interface LoginService {
    @GET("/auth")
    public void basicLogin(Callback<AuthObject> response);
}

这是执行调用的方法,主要是 activity 我通过事件总线获取对象。

public class AuthAPI {
    private Bus bus;
    LoginService loginService;

    public AuthAPI(String username, String password) {
        this.bus = BusProvider.getInstance().getBus();
        loginService = ServiceGenerator.createService(LoginService.class,
                CommonUtils.BASE_URL,
                username,
                password);
    }

    public void Login() {

        loginService.basicLogin(new Callback<AuthObject>() {
            @Override
            public void success(AuthObject authObject, Response response) {
                bus.post(authObject);
            }

            @Override
            public void failure(RetrofitError error) {
                AuthObject authObject = new AuthObject();
                authObject.setError(true);
                bus.post(authObject);
            }
        });
    }

}

这里是测试

@RunWith(MockitoJUnitRunner.class)
public class AuthCallTest extends TestCase {

    AuthAPI authAPI;

    @Mock
    private LoginService mockApi;

    @Captor
    private ArgumentCaptor<Callback<AuthObject>> cb;

    @Before
    public void setUp() throws Exception {
        authAPI = new AuthAPI("username", "password");
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testLogin() throws Exception {

        Mockito.verify(mockApi).basicLogin((cb.capture()));

        AuthObject authObject = new AuthObject();
        cb.getValue().success(authObject, null);

        assertEquals(authObject.isError(), false);
    }
}

当我启动测试时出现此错误

Wanted but not invoked:
mockApi.basicLogin(<Capturing argument>);
-> at AuthCallTest.testLogin(AuthCallTest.java:42)
Actually, there were zero interactions with this mock.

我做错了什么,这让我发疯 我试图遵循本指南但没有成功: http://www.mdswanson.com/blog/2013/12/16/reliable-android-http-testing-with-retrofit-and-mockito.html

有人帮帮我:(

问题是您在错误的时刻调用了 verifyverify 的目的是验证与 mockApi 的交互是否符合您的预期。所以通常你会看到类似的东西:

authApi.login();
Mockito.verify(mockApi).basicLogin((cb.capture()));

这也是错误消息告诉您的内容:verify 预期 basicLogin 被调用,但没有被调用。

那篇文章我也看了,感觉少了点什么。我实际上还没有理解参数捕获。所以帮不了你:)

这篇文章不是很清楚,因为它遗漏了设置步骤。通过访问文章中链接的 GitHub project,您可以看到完整的源代码,其中解释了那些缺失的步骤:

1) 代码示例是从测试 class 测试特定 activity 中提取的。作为设置的一部分(即在 @Before 中),它将 Activity 对 GitHub API 实现的引用替换为模拟实现。然后调用 Activity 的 onCreate().

2) 在 onCreate() 期间,activity 调用现在替换的 GitHub API,传入它的 Callback 对象。

前两个步骤解释了为什么每个测试开始时的 Mockito.verify(mockApi).repositories(Mockito.anyString(), cb.capture()); 步骤有效。由于测试是 运行 在 @Before 之后,mockApi 确实调用了它的 repositories() 方法。

代码的其余部分一旦到位就更容易理解。因为他只创建了一个 mockApi,但没有改变实际使用的 Callback,所以 activity 的内容被改变了。然后,其余代码通过检查 ListView 或 Toasts 来验证是否发生了这些更改。


所以要回答您的问题,您需要:

1) 在您的测试方法开始时,将 AuthAPI 的 loginService 对象替换为您的 mockApi 对象,然后调用 AuthAPI.Login().

2) 使用 verify() 来检查函数是否已被调用。

3) 创建样本 AuthObject 并将其传递给 cb.getValue().success() 函数。

4) 从您的 Bus 中获取 AuthObject 并断言它与您发送到 callback.success() 函数的相同。

这会测试您的 AuthAPI.Login() 是否正确地向您的 Bus 发送了将从 Retrofit 检索的 AuthObject


(我知道 SO 问题是很久以前写的,但是当我看到同一篇文章并且最近有同样的困惑时,我认为这个答案可能对其他人有用。)