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
有人帮帮我:(
问题是您在错误的时刻调用了 verify
:verify
的目的是验证与 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 问题是很久以前写的,但是当我看到同一篇文章并且最近有同样的困惑时,我认为这个答案可能对其他人有用。)
我从 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
有人帮帮我:(
问题是您在错误的时刻调用了 verify
:verify
的目的是验证与 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 问题是很久以前写的,但是当我看到同一篇文章并且最近有同样的困惑时,我认为这个答案可能对其他人有用。)