60

Espresso浅析和使用 - 腾讯云社区 - 腾讯云

 6 years ago
source link: https://cloud.tencent.com/community/article/520289?
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Espresso浅析和使用

修改于2017-11-17 03:27:08阅读 1.2K0
1620

Espresso是一个Google官方提供的Android应用UI自动化测试框架。Google希望,当Android的开发者利用Espresso写完测试用例后,能一边看着测试用例自动执行,一边享受一杯香醇Espresso(浓咖啡)。

Espress有3个特点:

  • 第一个收录在Android Testing Supporting Library底下的测试框架
  • 模拟用户的操作
  • 自动等待,直到UI线程Idle,才会执行测试代码

接下来,将从配置、写用例、运行一步步介绍Espresso的使用。

0. 项目配置

0.1 修改App的build.gradle

  • 在defaultConfig内增加,testInstrumentationRunner “android.support.test.runner.AndroidJUnitRunner”,用来运行脚本
  • 增加packagingOptions,避免编译时候License的冲突
  • 在dependencies中增加相关的引用(androidTestCompile只有在编译测试用例时候才会运行,普通编译不会)

下面是build.gradle中涉及到Espresso配置的内容

android {
    defaultConfig {
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
       }
    }
    packagingOptions {
        exclude 'LICENSE.txt'
    }
}
dependencies {
    // Espresso 相关的引用
    compile 'com.android.support:support-annotations:22.1.1'
    androidTestCompile 'com.android.support:support-annotations:22.1.1'
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.1'){
        exclude group: 'javax.inject'
    }
    androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.1'
    androidTestCompile 'com.android.support.test.espresso:espresso-contrib:2.1'
    androidTestCompile 'com.android.support.test:runner:0.2'
}

0.2 添加TestRunner

点击顶栏菜单Run->Edit Configurations,出现如下的窗口后,点击左上角的”+”,选择”Android Tests”;

修改新Configuration的名字,选中App Module,输入Runner,选择”Show chooer dialog”,点击”OK”完成

1. 写测试用例

1.1 三步曲

写UI自动化测试用例,归结起来就是3步:

定位View控件

操作View控件

校验View控件的状态

对应Espresso,就是以下3个方法的调用:

onView(ViewMatcher)
  .perform(ViewAction)
  .check(ViewAssertion);

其中,onView是用来定位View控件的,perform是操作控件的,check是校验View控件的状态。他们各自都需要再传入对应的参数分别如下:

ViewMatcher,有withId、withText、withClassName等等方法来定位View控件

ViewAction,有click()、longClick()、pressBack()、swipeLeft()等等方法来操作View控件

ViewAssertion,有isEnabled()、isLeftOf()、isChecked()等等方法来校验View控件状态

这里有ViewMatcherViewActionViewAssertion的Cheat Sheet。

1.2 完整测试用例代码

这是一个非常简单的测试用例,通过R.id.button定位控件,对它调用了一下click,最后校验控件是不是enabled状态。这里面有一些注解,@Rule修饰的是被测试的Activity@Test修饰的方法是测试用例。

@RunWith(AndroidJUnit4.class)public class MainActivityTest {    @Rule
    public ActivityTestRule mActivityRule = new ActivityTestRule(MainActivity.class);    @Test
    public void testTextViewDisplay() {
        onView(withId(R.id.button))
            .perform(click())
            .check(matches(isEnabled()));
    }
}

1.3 注意

Getting Started With Espresso 2.0这个视频中提到了2个写测试用例时的注意项:

避免Activity的层级跳转,测试用例尽量只在单个Activity内完成。Activity层级跳转越多,越容易出错

强烈不推荐,直接获取View的对象,调用View的方法来模拟用户操作。应该统一使用Espresso提供的方法

测试用例,特别是UI自动化测试用例,应该尽量保持逻辑简单,覆盖关键路径就足矣。因为UI变动是很频繁的,越复杂,维护成本就越高,投入产出比就会自然降低了。

2. 运行用例

  1. 在运行菜单中选择步骤0.2中设置的TestRunner,点击执行
  2. 测试用例模拟用户操作自动运行
  3. 测试用例执行完成,在Android Studio的控制台上,能看到如下的结果输出

其中,看到”Done 3 of 3”标识,一共3个检查点,都检查通过了。如果有检查不通过的话,右上角的绿色能量条会变成红色。

3.1 onData的使用

对于ListView,如果要操作其中的某一个item,特别是不可见状态的item,是不能通过上述的ViewMatch来定位的。我们都知道ListViewView是复用的,不可见状态的item并没有把内容绘制到View上。Espresso针对AdapterViewListView的父类),提供了onData来支持。

onData(ObjectMatcher)
  .DataOptions
  .perform(ViewAction)
  .check(ViewAssertion);

onData传入的是一个ObjectMather。首先假设ListView的Adapter中的Item的定义如下:

public static class Item {
    private final int value;
    public Item(int value) {
        this.value = value;
    }
    public String toString() {
        return String.valueOf(value);
    }
}

下面定义一个withValue()的方法,返回一个BoundedMatcher。而其中的matchesSafely()方法是用来判断match与否的,判断的逻辑实现都放在这里。

public static Matcher<Object> withValue(final int value) {
    return new BoundedMatcher<Object,
            MainActivity.Item>(MainActivity.Item.class) {
        @Override public void describeTo(Description description) {
            description.appendText("has value " + value);
        }
        @Override public boolean matchesSafely(
                MainActivity.Item item) {
            return item.toString().equals(String.valueOf(value));
        }
    };
}

有了上面的铺垫,测试用例写起来就水到渠成了。在id是R.id.listAdapterView中找到数据项是27,然后执行click()操作。

@Test
public void clickItem() {
    onData(withValue(27))
            .inAdapterView(withId(R.id.list))
            .perform(click());
    //Do the assertion here.
}

最后需要注意的是,onData()并不适用于RecyclerView,因为它不是继承自AdapterView。Espresso提供专门给RecyclerView使用的RecyclerViewActions

@Test 
public void clickItem() {
    onView(withId(R.id.recycler_view))
            .perform(
                    RecyclerViewActions.actionOnItemAtPosition(27, click()));
}

3.2 Idling Resource的使用

应用开发中很常见的一个场景是,点击某个按钮,发起网络请求,等请求回来后解析数据,更新界面。Espresso针对这种测试场景,提供了原生的支持。

假设被测Activity初始化后有一个耗时的数据加载过程,activity.isSyncFinished()方法判断数据加载是否已经完成。代码如下:

@Override
protected void onCreate(Bundle savedInstanceState) {
    //模拟耗时的数据加载
    new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
        @Override
        public void run() {
            mIsSyncFinished = true;
        }
    }, 5000);
}

private volatile boolean mIsSyncFinished = false;
public boolean isSyncFinished() {
    return mIsSyncFinished;
}

这种情况,Espresso提供了IdlingResource来保证数据加载完成了才开始执行测试用例代码。首先,需实现IdlingResource接口:

  • getName()方法返回的String是作为注册回调的Key,所以要确保唯一性
  • registerIdleTransitionCallback()的参数ResourceCallback会用做isIdleNow()时候的回调
  • isIdleNow()是否已经处于空闲状态,这里调用activity.isSyncFinished()方法来判断数据加载是否完成
private static class MyIdlingResource implements IdlingResource {
    private ResourceCallback mCallback = null;
    private MainActivity mActivity;

    MyIdlingResource(MainActivity activity) {
        mActivity = activity;
    }

    @Override
    public String getName() {
        return "MyIdlingResource";
    }

    @Override
    public void registerIdleTransitionCallback(ResourceCallback callback) {
        mCallback = callback;
    }

    @Override
    public boolean isIdleNow() {
        boolean isIdle = mActivity != null && mActivity.isSyncFinished();
        if (isIdle && mCallback != null) {
            mCallback.onTransitionToIdle();
        }
        return isIdle;
    }
}

MyIdlingResource需要在恰当的时机注册和反注册。@Before@After是依照JUnit4的惯例,分别在用例执行之前和之后去注册和反注册。那么,如下测试用例执行的过程是:

  1. 测试用例启动,注册MyIdlingResource
  2. 启动被测Activity
  3. Activity初始化,启动数据加载过程
  4. Activity数据加载完成,执行测试用例方法testTextViewDisplay()
  5. 测试用例结束,反注册MyIdlingResource

可见,IdlingResource能够保证流转到Idle状态,才会执行测试代码:

@Test
public void testTextViewDisplay() {
    onView(withText("Show SnackBar")).check(ViewAssertions.matches(isDisplayed()));
}

@Before
public void registerIntentServiceIdlingResource() {
    Activity activity = mActivityRule.getActivity();
    idlingResource = new MyIdlingResource((MainActivity) activity);
    Espresso.registerIdlingResources(idlingResource);
}

@After
public void unregisterIntentServiceIdlingResource() {
    Espresso.unregisterIdlingResources(idlingResource);
}

3.3. 执行原理

本文开头提到Espresso其中一个特点,无需主动写Sleep等待UI事件的执行和UI的绘制。原因是,Espresso的用例运行过程是只有当UI线程IDLE和UI队列没有需要执行的事件时,Espresso的测试代码才会被执行。使用方无需写Sleep逻辑等待UI绘制完成。以下是Espresso测试用例执行简易的流程图,帮助理解:

引用官方介绍的一段话,Espresso的目标受众是开发者。希望更多的团队能够实现Google的期许最大化利用Espresso,把Bug扼杀在摇篮中。

Target Audience

Espresso is targeted at developers, who believe that automated testing is an integral part of the development lifecycle. While it can be used for black-box testing, Espresso’s full power is unlocked by those who are familiar with the codebase under test.

Getting Started With Espresso 2.0:https://www.youtube.com/watch?v=TGU0B4qRlHY

Advanced Android Espresso:https://realm.io/news/chiu-ki-chan-advanced-android-espresso-testing/

Android Espresso 测试框架探究:http://blog.csdn.net/weijianfeng1990912/article/details/51540468

Android自动化测试-AdapterView的测试:https://segmentfault.com/a/1190000004392396

Android单元测试研究与实践:http://tech.meituan.com/Android_unit_test.html

文章来自: QQ音乐技术团队 公众号


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK