如何单元测试改装api调用?

时间:2015-09-03 11:34:46

标签: android unit-testing junit android-gradle retrofit

我正在尝试为每个代码块集成单元测试用例。 但是,我在为通过改造进行的api调用添加测试用例时遇到了问题。

JUnit编译器永远不会执行 CallBack函数中的代码

还有另一个选项可以将所有api调用同步用于测试目的,但对于我的应用中的每个案例都不可能。

我该如何解决这个问题?我必须以任何方式在api调用中添加测试用例。

6 个答案:

答案 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可能也有帮助。

  • 我的建议是不要在android应用中对API响应进行测试。为此,有许多外部tools

答案 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);

    }