通过Espresso测试异步代码

本篇讲如何通过Espresso实现异步测试.

概述

如果没有框架的支持测试异步代码还是非常具有挑战性的 ! 在 Espresso 之前典型的做法就是等待预定的时间.或者在测试代码使用 CountDownLatch 类的实例, 并在异步处理完成时发出信号. 而 Espresso 使得异步测试变得容易很多,因为它自动检测 AsynchronousTask 后面的线程池.它还监视用户界面的事件队列, 仅在没有任务运行时才继续进行测试.

示例

下面示例根据官方的异步测试案例进行了简化处理:

Espresso 提供了 IdlingResource 接口用来检视当前资源是否空闲.我们可以根据这个接口进行简单的封装让其更适合我们使用.

在开始下面内容之前需要检查一下你的 Gradle 文件是否添加了 implementation ‘com.android.support.test.espresso:espresso-intents:3.0.1’ 依赖, 这个依赖是供 src/main/java 目录下访问的,用于我们接下来封装异步测试的工具类.

src/main/java 目录下创建 SimpleCountingIdlingResource 类,实现 IdlingResource 接口. 该类实现整个项目异步请求个数的检视.

package com.lulu.androidtestdemo.espresso.utils;

import android.support.test.espresso.IdlingResource;
import java.util.concurrent.atomic.AtomicInteger;
/**
 * Created by zhanglulu on 2018/3/8.
 */
public class SimpleCountingIdlingResource implements IdlingResource {

    private final String mResourceName;

    //这个counter值就像一个标记,默认为0
    private final AtomicInteger counter = new AtomicInteger(0);

    private volatile ResourceCallback resourceCallback;

    public SimpleCountingIdlingResource(String resourceName) {
        mResourceName = resourceName;
    }

    @Override
    public String getName() {
        return mResourceName;
    }

    @Override
    public boolean isIdleNow() {
        return counter.get() == 0;
    }

    @Override
    public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
        this.resourceCallback = resourceCallback;
    }

    //每当我们开始异步请求,把counter值+1
    public void increment() {
        counter.getAndIncrement();
    }

    //当我们获取到网络数据后,counter值-1;
    public void decrement() {
        int counterVal = counter.decrementAndGet();
        //如果这时counter == 0,说明异步结束,执行回调。
        if (counterVal == 0) {
            //
            if (null != resourceCallback) {
                resourceCallback.onTransitionToIdle();
            }
        }
        if (counterVal < 0) {
            //如果小于0,抛出异常
            throw new IllegalArgumentException("Counter has been corrupted!");
        }
    }
}

在同一目录下添加 EspressoIdlingResource 类, 作为 SimpleCountingIdlingResource 的管理类,负责该类的创建和管理.

public class EspressoIdlingResource {
    private static final String RESOURCE = "GLOBAL";

    private static SimpleCountingIdlingResource mCountingIdlingResource =
            new SimpleCountingIdlingResource(RESOURCE);

    public static void increment() {
        mCountingIdlingResource.increment();
    }

    public static void decrement() {
        mCountingIdlingResource.decrement();
    }

    public static IdlingResource getIdlingResource() {
        return mCountingIdlingResource;
    }
}

现在在我们的待测试的 Activity 中只需在异步代码开始时添加 EspressoIdlingResource.increment(), 结束时添加 EspressoIdlingResource.decrement() , 并添加 getIdlingResource() 方法方便我们在测试方法中调用.(可能这样做对代码有耦合,但是官方的做法确实如此).如果是在真正的项目中, 可以考虑将其放在BaseActivity中,下面就是待测试的 Activity.

public class TestActivity extends AppCompatActivity {

    private TextView text;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        text = findViewById(R.id.text);
    }

    public void onButtonClick(View view) {
        EspressoIdlingResource.increment();
        Toast.makeText(this, "View Clicked", Toast.LENGTH_SHORT).show();
        switch (view.getId()) {
            case R.id.my_view:
                new Thread(() -> {
                    try {
                        Thread.sleep(2000);
                        mHandler.sendEmptyMessage(0);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }).start();
                break;
            case R.id.my_view2:
                text.setText("Running");
                break;
        }

    }

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            text.setText("Done");
            if (!EspressoIdlingResource.getIdlingResource().isIdleNow()) {
                EspressoIdlingResource.decrement();
            }
        }
    };

    @VisibleForTesting
    public IdlingResource getIdlingResource() {
        return EspressoIdlingResource.getIdlingResource();
    }
}

下面就是对应的测试方法, 检查 R.id.text 代码将会在子线程执行完成之后进行验证.

/**
 * Created by zhanglulu on 2018/3/8.
 */
@RunWith(AndroidJUnit4.class)
public class EspressoAsyncTest {
    private IdlingResource idlingresource;

    @Rule
    public ActivityTestRule<TestActivity> activityRule = new ActivityTestRule(TestActivity.class);

    @Before
    public void before() {
        //调用Activity中我们已经设置好的getIdlingresource()方法,获取Idlingresource对象
        idlingresource = activityRule.getActivity().getIdlingResource();
        IdlingRegistry.getInstance().register(idlingresource);
    }
    @After
    public void after() {
        IdlingRegistry.getInstance().unregister(idlingresource);
    }
    @Test //is(String.class), is("Done")
    public void testAsync() {
        onView(withId(R.id.my_view)).perform(click());
        onView(withId(R.id.text)).check(matches(withText("Done")));
    }
}

《通过Espresso测试异步代码》 敲行代码再睡觉

    原文作者:changer0
    原文地址: https://www.jianshu.com/p/5c5bb0ff8d19
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞