我正在尝试为每个可能的代码块集成单元测试用例。但是我在为api调用添加测试用例时面临的问题是通过改造。
JUnit编译器从不执行 CallBack函数中的代码。
还有另一种选择,使所有api调用同步用于测试目的,但这并不适用于我的应用程序中的每种情况。
我该如何解决这个问题?我必须通过任何方式在api调用中添加测试用例。
如果您使用.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();
}
}
}
我使用Mockito, robolelectric和Hamcrest库测试我的Retrofit回调。
首先,在模块的build.gradle中设置库堆栈:
dependencies {
testCompile 'org.robolectric:robolectric:3.0'
testCompile "org.mockito:mockito-core:1.10.19"
androidTestCompile 'org.hamcrest:hamcrest-library:1.1'
}
在项目的全局构建中。Gradle添加以下行到buildscript依赖项:
classpath 'org.robolectric:robolectric-gradle-plugin:1.0.1'
然后在Android Studio中进入"Build variables"菜单(为了快速找到它,按Ctrl+Shift+A并搜索它),并将"Test Artifact"选项切换到"Unit Tests"。Android studio会将你的测试文件夹切换到com.your。包(测试)"(而不是androidTest).
Ok。设置完成了,是时候编写一些测试了!
假设你有一些改进的api调用来检索一个对象列表,这些对象需要放入某个适配器中用于RecyclerView等。我们想测试适配器是否在成功调用时填充了适当的项目。要做到这一点,我们需要切换您的Retrofit接口实现,您使用它来使用mock进行调用,并利用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));
}
}
通过右键单击测试类并点击run来继续测试。
就是这样。我强烈建议使用Robolectric(带有Robolectric gradle插件)和Mockito,这些库使测试android应用程序变得更加容易。我从下面的博客文章中学到了这个方法。还有,参考这个答案
Update:如果您正在使用RxJava的Retrofit,请查看我的其他答案
-
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(); } }
-
这个测试样本可能也有帮助。
- 我的建议是不要在android应用程序中执行API响应测试。有许多外部工具可以做到这一点。
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
或者你可以在await调用
中指定超时 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);
}
如果已经封装retrofit2.0与rx与restful
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>>
你的服务只回调一个同步请求Observable
val it = service.getData().blockingSingle()
assertTrue(it.status == SUCCESS_CODE)
正如@Islam Salah所说:
JUnit框架永远不会执行CallBack函数中的代码,因为执行的主线程在检索到响应之前就终止了。
你可以用awaitility
来解决这个问题。在StackOverflow上查看这个答案