单元测试系列之六Espresso

482 阅读2分钟

之前介绍的都是逻辑测试,不用运行在模拟器或者真机上,做Android开发测试,免不了要进行UI测试,而大部分的UI更新都是异步的,这时候要进行单元测试就需要想别的方法了。这里介绍一下框架,espresso,体积小,运行快。

对同步更新UI的方法进行测试并不难,比如点击按钮,点击后改变Textview的内容,这很简单。但是更多时候我们用到的是异步更新UI。如果直接进行测试,大概率会报错,因为在执行测试代码来测试数据是否展示在UI上时,异步数据很有可能还没有获取到。针对这个问题,我们借助espresso,使用框架中的SimpleCountingIdlingResourceEspressoIdlingResourceManager可以实现异步更新UI的测试。

  1. 首先在activity中添加这样一个方法,这个方法是给测试时提供获取IdlingResource的接口
  @VisibleForTesting
   public SimpleCountingIdlingResource getCountingIdlingResource() {
       return EspressoIdlingResourceManager.getIdlingResource();
  }
  1. 当我们开始异步请求前,将IdlingResource的标记加一,这就意味着已经开始异步了
//在开始异步请求前添加这行代码,意味着开始了异步
EspressoIdlingResourceManager.increment();

当请求结束,我们再添加这行代码,说明网络请求结束:

//异步加载成功,结束异步
 if (!EspressoIdlingResourceManager.getIdlingResource().isIdleNow()) {
    EspressoIdlingResourceManager.decrement();
 }

然后回到测试类中,添加如下测试代码:

  private IdlingResource idlingresource;
​
   @Test
   public void testAsync2() throws Exception {
       //调用Activity中我们已经设置好的getIdlingresource()方法,获取Idlingresource对象
       idlingresource = ((MainActivity) activityTestRule.getActivity()).getCountingIdlingResource();
​
       // 操作:点击Button
       onView(withId(R.id.async_btn))
              .perform(click());
​
​
       //注册异步监听,当该idlingresource中的counter标记值为0时才进行接下来的测试代码
       Espresso.registerIdlingResources(idlingresource);
​
       // 未注册idlingResource时,立即进行test,此时异步并未结束,报错(tests failed)
       onView(withId(R.id.textview))
              .check(matches(withText("异步测试结果显示")))
              .check(matches(isDisplayed()));
​
       //我们在测试结束后取消注册,释放资源
       Espresso.unregisterIdlingResources(idlingresource);
  }
​

上面提到了两个类的源码如下:

EspressoIdlingResourceManager.class

public class EspressoIdlingResourceManager {

    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 SimpleCountingIdlingResource getIdlingResource() {
        return mCountingIdlingResource;
    }
}

SimpleCountingIdlingResource.class

public final 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!");
        }
    }
}

这样,我们就完成了异步更新UI的测试。