使用 Espresso 隔离测试视图

在这篇文章里,我将会告诉你为何并且如何使用 Espresso 在 Android 设备上测试你的自定义视图。

你可以使用 Espresso 来一次性测试所有界面或流程。这些测试用例会启动某个页面,并像用户一般执行操作,包括等待数据的加载或跳转到其他页面。

这样做是非常有用的,因为你需要端到端的测试用例来验证常见的用户使用流程。这些自动化测试应该定期地执行,从而可以节约手工 QA 的时间来进行探索性测试。

即便如此,这些不是可以频繁运行的测试。运行一整套可能会花费数小时的时间(想象一下验证媒体内容的脱机同步),所以你可以选择在夜间运行它们。

这很困难,因为这些类型的测试包含了多个潜在的故障点。理想情况是,当某个测试失败时,你会希望它是由于单个逻辑断言而导致的。

大多数(或者说很多)可以引入的回归测试点都在 UI 上。这些问题很可能是十分细微的,以至于我们在添加新特性时并不会注意到,但是敏锐的 QA 团队却往往可以。

这样就浪费太多时间了。

你能做些什么?

让我们来看下如何使用 Espresso 来测试正确地绑定了数据的视图。

在 Novoda 里,我们编写的大多数视图都是继承自 Android 已有的 View 和 ViewGroup 类。这些视图一般只会暴露了一到两个方法用来绑定回调函数和数据对象/视图模型,如下所示:

public class MovieItemView extends RelativeLayout {
private TextView titleTextView;
private Callback callback; public void attach(Callback callback) {
this.callback = callback;
} public void bind(Movie movie) {
titleTextView.setText(movie.name());
setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
callback.onClick(movie);
}
});
}
}

他们将 UI 的逻辑部分组合在一起,并且通常还包含来自业务领域的命名规范。在

Novoda 的页面布局中你很少会看到“原始”的 Android 视图。

让我们使用 BDD 风格来编写这些视图测试,比如“当 MovieItemView 被绑定到 Edward Scissorhands 上,标题就被设置成 Edward Scissorhands”或者“MovieItemView 被绑定到 Edward Scissorhands 上,当点击视图时,onClick(Edward Scissorhands) 就会被调用”,等等。(译者注:BDD(Behaviour Driven Development),倾向于断言被测对象的行为特征而非输入输出。一个典型的 BDD 的测试用例包活完整的三段式上下文,测试大多可以翻译为 Given-When-Then 的格式,即某种场景下,发生了事件,导致了什么结果。)

难道不能使用单元测试来捕获这些问题吗?

如果你正在使用像 MVP 或者 MVVM 这样可被单元测试的表现模式,为什么还需要 Espresso 来运行这些测试呢?

首先,让我们来看一下展示信息的流程并且描述一下目前所能做的测试,然后再看看使用 Espresso 测试能多做些什么。

  • Presenters 订阅发送事件的数据生成器

  • 事件可以处于加载中空闲错误状态,并且可能带有要展示的数据

  • Presenters 将使用 display(List<Movie>)displayCachedDataWhileLoading(List<Movie>)displayEmptyScreen() 等方法将这些事件转发给“displayers”(MVP 中的“View”)。

  • displayers 的具体实现类将显示/隐藏 Android 视图,并执行诸如 moviesView.bind(List<Movie>) 之类的操作

你可以对 presenters 进行单元测试,验证是否调用了 displayers 正确的方法并且带有正确的参数。

你可以用相同的方式测试 displayers 吗?是的,你是可以模拟 Android 视图,并验证是否调用了正确的方法。但这样的粒度并不是我们想要的:

  • displayer 可能确实构建或更新了 RecyclerView 或 ViewPager 适配器,但这并不代表显示了正确的内容。

  • Android 视图是通过在代码中加载 XML(布局和样式)设置的;验证方法的调用不足以断言显示的内容是否正确

设置测试用例

就从使用 espresso-support 这个库开始吧。

在你的 build.gradle(JCenter 可用)里添加依赖

debugCompile 'com.novoda:espresso-support-extras:0.0.3'
androidTestCompile 'com.novoda:espresso-support:0.0.3'

extras 依赖包中包含了 ViewActivity,在测试时需要将其添加到你的应用中。你可以在该 Activity 持有想要使用 Espresso 测试的单一视图。

核心部分(包含自定义测试规则)只需要作为 androidTest 依赖中的一部分。

ViewTestRule 使用方法与 ActivityTestRule 类似。只不过是将传递的参数从想要启动的 Activity 类替换成了包含你想要测试的视图的布局文件:

@RunWith(AndroidJUnit4.class)publicclassMovieItemViewTest{
@Rule
public ViewTestRule<MovieItemView> viewTestRule=newViewTestRule<>(R.layout.test_movie_item_view);
...

你可以使用 ViewTestRule<MovieItemView> 指定根布局的视图类型。

ViewTestRule 继承了 ActivityTestRule<ViewActivity>,所以它总会打开 ViewActivitygetActivityIntent() 被重写了,所以你可以将 R.layout.test_movie_item_view 作为 Intent 的附加数据传递给 ViewActivity

你可以在测试中使用 Mockito 代替回调函数。

@Rule
public MockitoRule mockitoRule = MockitoJUnit.rule(); @Mock
MovieItemView.Listener movieItemListener; @Before
publicvoidsetUp(){
MovieItemView view = viewTestRule.getView();
view.attachListener(movieItemListener);
...
}

ViewTestRule 有一个 bindViewUsing(Binder) 方法,该方法会返回视图的引用,以便你与之进行交互。当你使用 viewTestRule.getView() 直接访问视图时,你会希望与视图的所有交互都是在主线程上执行的,而非测试线程。

@Before
public void setUp() {
MovieItemView view = viewTestRule.getView();
view.attachListener(movieItemListener);
viewTestRule.bindViewUsing(new ViewTestRule.Binder<MovieItemView>() {
@Override
public void bind(MovieItemView view) {
view.bind(EDWARD_SCISSORHANDS);
}
});
}

准备测试

从用户的角度上来看,应用其实只做了两件事情:

  • 展示信息

  • 响应用户的操作

要为这两种情况编写测试,你可以先从使用标准的 Espresso ViewMatchers 和 ViewAssertions 语句断言是否显示正确的信息开始:

@Test
public void titleSetToMovieName() {
onView(withId(R.id.movie_item_text_name))
.check(matches(withText(EDWARD_SCISSORHANDS.name)));
}

接着,你应该确保用户的操作触发了正确的点击事件,并且具有正确的参数:

@Test
public void clickMovieItemView() {
onView(withClassName(is(MovieItemView.class.getName())))
.perform(click()); verify(movieItemListener)
.onClick(eq(EDWARD_SCISSORHANDS));
}

到这里就完成了,希望这些知识对你有用。

在接下来的文章里,我会介绍如何使用 Espresso 测试视图时支持 TalkBack 服务(译者注:Talkback 是一款由谷歌官方开发的系统工具软件,它的定位是帮助盲人或者有视力障碍的用户提供语言辅助)。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOSReact前端后端产品设计 等领域,想要查看更多优质译文请持续关注 掘金翻译计划

[译] 使用 Espresso 隔离测试视图的更多相关文章

  1. Python_线程、线程效率测试、数据隔离测试、主线程和子线程

    0.进程中的概念 三状态:就绪.运行.阻塞 就绪(Ready):当进程已分配到除CPU以外的所有必要资源,只要获得处理机便可立即执行,这时的进程状态成为就绪状态. 执行/运行(Running)状态:当 ...

  2. [译]Vulkan教程(28)Image视图和采样器

    [译]Vulkan教程(28)Image视图和采样器 Image view and sampler - Image视图和采样器 In this chapter we're going to creat ...

  3. 使用Django 测试客户端一起测试视图,模板和URL

    Django 测试客户端 self.client.get(url)  测试客户端是Django中TestCase类的一个属性名. 至此已经在网站中可以创建一个清单了. 那么,现在是可以在unit te ...

  4. 使用Microsoft Fakes隔离测试代码

    在单元测试(Unit Test)中我们遇到的问题之一是:假如被测试组件(类或项目)为A,组件A依赖于组件B,那么在组件A的单元测试ATest中测试A时,也需要依赖于B,在B发生改动后,就可能影响到A的 ...

  5. 如何正确使用Espresso来测试你的Android程序

    UI测试在Android平台上一直都是一个令人头痛的事情, 由于大家平时用的很少, 加之很多文档的缺失, 如果很多东西从头摸索,势必踩坑无数. 自Android24正式淘汰掉了Instrumentat ...

  6. 使用Profile和Resources Filter隔离测试环境

    转自:http://juvenshun.iteye.com/blog/206825 Maven能够帮我们很好的管理测试,我们可以在 src/test/java 和 src/test/resources ...

  7. [译]Angular-ui 之 多命名视图(Multiple Named Views)

    ◄上一篇 (Nested States & Nested Views)     下一篇 (URL Routing) ► 你可以给你的视图命名,以便可以在单个模版当中拥有一个以上的 ui-vie ...

  8. oracle 唯独测试视图

    --建立用户分配权限 create user groper identified by groper / grant connect,resource to groper / grant create ...

  9. [译] ConstraintLayout 可视化[Design]编辑器(这到底是什么)[第四部分]

    原文地址:Testing Views in Isolation with Espresso 原文作者:Ataul Munim 译文出自:掘金翻译计划 译者:yazhi1992 校对者:lovexiao ...

随机推荐

  1. C#中From窗体判断提示框是否

    int ret(变量名1) = command.ExecuteNonQuery(); if (ret(变量名1) > 0)//判断变量名是否大于0 { DialogResult dr(变量名2) ...

  2. fist-冲刺第二天随笔

    这个作业属于哪个课程 https://edu.cnblogs.com/campus/fzzcxy/2018SE1 这个作业要求在哪里 https://edu.cnblogs.com/campus/fz ...

  3. 对称加密之---AES加密

    工作中常会需要让数据传输前进行加密处理.这次用到的是AES加密.AES加密中,需要注意到坑还是挺多的.对AES也进行了一番了解,发现里面的东西真的是注意的太多了.今天只是整理了一种简单的加密格式,工作 ...

  4. 老猿学5G:3GPP和中国移动5G计费架构概览

    ☞ ░ 前往老猿Python博文目录 ░ 一.引言 老猿学5G这个专栏主要记录笔者因工作原因学习了解5G计费相关知识,文章按时间顺序循序渐进的介绍5G基础概念以及5G计费相关知识,该专栏前期已经完结, ...

  5. 第8.6节 Python类中的__new__方法深入剖析:调用父类__new__方法参数的困惑

    上节<第8.5节 Python类中的__new__方法和构造方法__init__关系深入剖析:执行顺序及参数关系案例详解>通过案例详细分析了两个方法的执行顺序,不知大家是否注意到了,在上述 ...

  6. PyQt(Python+Qt)学习随笔:QTableWidget的构造方法

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 QTableWidget有2个构造方法: QTableWidget(QWidget parent = ...

  7. Hello TLM

    前言 目标 了解TLM程序的基本过程.TLM的英文全称是Transaction Level Modeling,中文翻译为事务级建模.它是在SystemC基础上的一个扩展库. 功能描述 模块A向模块B发 ...

  8. Github 美化设置个人主页

    起因是发现自己follow的大师傅个人主页跟普通的不太一样: 猜测应该是Github啥时候出现的新功能,查了一下,发现可以通过创建同名仓库来实现个人主页的美化设置 首先在 GitHub 上建立一个与自 ...

  9. ⑤SpringCloud 实战:引入Zuul组件,开启网关路由

    这是SpringCloud实战系列中第4篇文章,了解前面第两篇文章更有助于更好理解本文内容: ①SpringCloud 实战:引入Eureka组件,完善服务治理 ②SpringCloud 实战:引入F ...

  10. Ubuntu 16.04 系统基础开发环境搭建

    1.安装 Git sudo apt-get update sudo apt-get install git Do you want to continue? [Y/n] Y git --version ...