我正在尝试为每个代码块集成单元测试用例。 但是,我在为通过改造进行的api调用添加测试用例时遇到了问题。
JUnit编译器永远不会执行 CallBack函数中的代码。
还有另一个选项可以将所有api调用同步用于测试目的,但对于我的应用中的每个案例都不可能。
我该如何解决这个问题?我必须以任何方式在api调用中添加测试用例。
答案 0 :(得分:23)
我使用Mockito,Robolectric和Hamcrest库测试我的Retrofit回调。
首先,在模块的build.gradle中设置lib堆栈:
dependencies {
testCompile 'org.robolectric:robolectric:3.0'
testCompile "org.mockito:mockito-core:1.10.19"
androidTestCompile 'org.hamcrest:hamcrest-library:1.1'
}
在jour项目的global build.gradle中,将以下行添加到buildscript依赖项:
classpath 'org.robolectric:robolectric-gradle-plugin:1.0.1'
然后在Android Studio中输入“Build Variants”菜单(快速找到它,按Ctrl + Shift + A并搜索它),并将“Test Artifact”选项切换为“Unit Tests”。 Android studio会将您的测试文件夹切换为“com.your.package(test)”(而不是androidTest)。
确定。设置完毕,是时候写一些测试了!
假设您已经有一些改进的api调用来检索需要放入RecyclerView等适配器的对象列表。我们想测试适配器是否在成功调用时填充了正确的项目。 要做到这一点,我们需要切换你用于使用模拟调用的Retrofit接口实现,并利用Mockito ArgumentCaptor类做一些假响应。
@Config(constants = BuildConfig.class, sdk = 21,
manifest = "app/src/main/AndroidManifest.xml")
@RunWith(RobolectricGradleTestRunner.class)
public class RetrofitCallTest {
private MainActivity mainActivity;
@Mock
private RetrofitApi mockRetrofitApiImpl;
@Captor
private ArgumentCaptor<Callback<List<YourObject>>> callbackArgumentCaptor;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
ActivityController<MainActivity> controller = Robolectric.buildActivity(MainActivity.class);
mainActivity = controller.get();
// Then we need to swap the retrofit api impl. with a mock one
// I usually store my Retrofit api impl as a static singleton in class RestClient, hence:
RestClient.setApi(mockRetrofitApiImpl);
controller.create();
}
@Test
public void shouldFillAdapter() throws Exception {
Mockito.verify(mockRetrofitApiImpl)
.getYourObject(callbackArgumentCaptor.capture());
int objectsQuantity = 10;
List<YourObject> list = new ArrayList<YourObject>();
for(int i = 0; i < objectsQuantity; ++i) {
list.add(new YourObject());
}
callbackArgumentCaptor.getValue().success(list, null);
YourAdapter yourAdapter = mainActivity.getAdapter(); // Obtain adapter
// Simple test check if adapter has as many items as put into response
assertThat(yourAdapter.getItemCount(), equalTo(objectsQuantity));
}
}
通过右键单击测试类并点击运行来继续测试。
就是这样。我强烈建议使用Robolectric(使用robolectric gradle插件)和Mockito,这些库使得测试Android应用程序变得更容易。 我从以下blog post中学到了这种方法。另请参阅this answer。
更新:如果您正在使用RxJava进行Retrofit,请查看my other answer on that。
答案 1 :(得分:16)
如果使用 .execute()而不是 .enqueue(),它会使执行同步,因此测试可以正常运行,而无需导入3个不同的库和添加任何代码或修改构建变体。
像:
public class LoginAPITest {
@Test
public void login_Success() {
APIEndpoints apiEndpoints = RetrofitHelper.getTesterInstance().create(APIEndpoints.class);
Call<AuthResponse> call = apiEndpoints.postLogin();
try {
//Magic is here at .execute() instead of .enqueue()
Response<AuthResponse> response = call.execute();
AuthResponse authResponse = response.body();
assertTrue(response.isSuccessful() && authResponse.getBearer().startsWith("TestBearer"));
} catch (IOException e) {
e.printStackTrace();
}
}
}
答案 2 :(得分:1)
JUnit框架从不执行CallBack函数中的代码,因为执行的主线程在检索响应之前终止。您可以使用CountDownLatch
,如下所示:
@Test
public void testApiResponse() {
CountDownLatch latch = new CountDownLatch(1);
mApiHelper.loadDataFromBackend(new Callback() {
@Override
public void onResponse(Call call, Response response) {
System.out.println("Success");
latch.countDown();
}
@Override
public void onFailure(Call call, Throwable t) {
System.out.println("Failure");
latch.countDown();
}
});
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
此test sample可能也有帮助。
答案 3 :(得分:1)
如果已经用rx封装了retrofit2.0并实现了宁静
open class BaseEntity<E> : Serializable {
/*result code*/
var status: Int = 0
/**data */
var content: E? = null
}
和服务器api请求
@GET(api/url)
fun getData():Observable<BaseEntity<Bean>>
您的服务仅回拨一个同步请求可观察
val it = service.getData().blockingSingle()
assertTrue(it.status == SUCCESS_CODE)
答案 4 :(得分:1)
作为@Islam Salah said:
<块引用>JUnit 框架从不执行 CallBack 函数中的代码,因为执行的主线程在检索到响应之前终止。
您可以使用 awaitility
来解决问题。查看 StackOverflow 上的 this 答案。
答案 5 :(得分:0)
Junit不会等待异步任务完成。您可以使用CountDownLatch阻止线程,直到收到来自服务器的响应或超时。
您可以使用CountDownLatch。 由于对countDown()方法的调用,当前的await方法一直阻塞,直到当前计数达到零为止,此后,所有等待线程被释放,随后的所有await调用将立即返回。
//Step 1: Do your background job
latch.countDown(); //Step 2 : On completion ; notify the count down latch that your async task is done
latch.await(); // Step 3: keep waiting
或者您可以在等待呼叫中指定超时
try {
latch.await(2000, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
示例测试用例
void testBackgroundJob() {
Latch latch = new CountDownLatch(1);
//Do your async job
Service.doSomething(new Callback() {
@Override
public void onResponse(){
ACTUAL_RESULT = SUCCESS;
latch.countDown(); // notify the count down latch
// assertEquals(..
}
});
//Wait for api response async
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
assertEquals(expectedResult, ACTUAL_RESULT);
}