如何进行单元测试 Requfit api 调用?

我试图为每一块可能的代码集成单元测试用例。 但是我在添加通过改造产生的 api 调用的测试用例时遇到了一些问题。

JUnit 编译器从不执行 回调函数中的代码

还有另一个选择,使所有的 api 调用 同步测试的目的,但这不可能在我的应用程序的每个情况下。

我怎样才能解决这个问题呢? 我必须通过任何方式在 api 调用中添加测试用例。

58251 次浏览

我使用 Mockito、 robolectic 和 Hamcrest 图书馆来测试我的 Revifit 回调函数。

首先,在模块的 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 Test”。Android 工作室会将测试文件夹切换到“ com.your.package (test)”(而不是 androidTest)。

好了,设置完成,是时候编写一些测试了!

假设您有一些改造 api 调用来检索需要放入某个适配器中的对象列表,以便用于回收视图等。我们想测试适配器是否在成功呼叫时被适当的项目填充。 要做到这一点,我们将需要切换您用于使用 mock 进行调用的 Afterfit 接口实现,并利用 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 来继续测试。

就是这样。我强烈建议使用 Robolecric (带有 robolectic gradle plugin)和 Mockito,这些库使测试 android 应用程序变得更加容易。 我从下面的 博客文章中学到了这个方法。

更新 : 如果您正在使用 RxJava 的卢克菲特,也可以查看 我的另一个回答

如果您使用 。执行()而不是 . 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();
}


}


}
  • 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();
    }
    }
    
  • This test sample may be helpful too.

  • My advice isn't to perform testing for the API responses in the android app. There are many external tools for this.

如果已经封装了2.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>>

您的服务回调只有一个同步请求可观察

val it = service.getData().blockingSingle()
assertTrue(it.status == SUCCESS_CODE)

Junit 不会等待异步任务完成。您可以使用 CountDownLatch (不需要外部库的优雅解决方案)来阻塞线程,直到收到来自服务器或超时的响应。

你可以使用 CountDownLatch。 由于对 count Down ()方法的调用,等待方法会一直阻塞,直到当前计数达到零为止,然后释放所有等待线程,并立即释放任何后续的等待返回调用。

//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);


}

作者@Islam Salah :

JUnit 框架从不执行 CallBack 函数中的代码,因为执行的主线程在检索响应之前终止。

您可以使用 awaitility来解决这个问题。请查看 StackOverflow 上的 这个答案。