全栈测试:平衡单元测试和端到端测试

全栈开发人员的特点是能够从头到尾交付并发布一个特性。教程和书籍常常侧重于搭建全栈开发环境和让测试能够进行所需要的“管件(plumbing)”(我综合运用了Angular、Rails、Bootstrap和Postgres)。但对于如何贯穿整个Web开发栈进行应用程序测试,却常常缺少指导。让我们深入研究下这篇文章。我们将学习如何充分利用端到端测试,包括对测试什么以及如何保证那些测试的可靠性和可维护性进行指导。我们还将谈及单元测试以及它们在端到端测试策略中的作用。但首先,我们要理解编写测试的根本目的。\

从根本上讲,测试是为了确保应用程序的行为符合开发者的意愿。它们是自动化的脚本,执行代码并检查其行为是否符合预期。测试编写得越好,就越可以依赖它们为部署把关。如果测试不充分,就需要一个QA团队或者发布有缺陷的软件(两者均意味着用户获得价值的速度比理想情况慢许多)。如果测试充分,就可以自信而快速地发布,不需要批准或者像QA那样缓慢的人工过程。\

对于编写的测试,还必须权衡未来的可维护性。应用程序会变,因此测试也会变。在理想情况下,测试的修改与软件的修改是成正比的。如果你修改了一条错误信息,那么你不会希望大量重写测试套件。但是,如果你彻底地修改了一个用户流程,那么可以预料,将有大量的测试需要重写。\

实际上,这意味着你无法将所有测试都作为端到端的全面集成测试,但是你也不能只进行少得可怜的单元测试。这就关乎如何达成那种平衡。\

测试的类型

\

测试的种类很多,但对于本文而言,我们就谈论两类:端到端测试和单元测试。\

端到端测试模拟用户行为。在Web应用程序中,他们会启动服务器,打开浏览器,到处点击,断言浏览器中发生了特定的事情,让我们相信功能可以正常运行。这些测试会给我们巨大的信心,但是它们缓慢而脆弱,并且同用户界面紧密地耦合在了一起。\

单元测试根据代码单元的公共API运行它们。这些测试需要创建一个类的实例,使用特定的输入调用它的方法,断言被调用的方法达到了预期的效果(通常是返回了预期的输出)。这些测试快速而稳定,并且不会同系统的其他部分紧密地耦合在一起。不过,它们无法让你相信整个系统可以正常运行——只是测试过的代码单元可以正常运行。\

构建一项特性的任务就是要在两类测试之间找到恰当的平衡点。如果端到端测试太多,那么未来修改应用程序就会痛苦而缓慢。如果太少,那么一些不易觉察的缺陷就会进入到生产环境,即使快速测试套件的代码覆盖率为100%。\

从用户体验入手

\

你的软件是向某个用户提供服务,因此,那个用户应该推动你的工作。我不建议使用测试来设计用户体验,因此,要在编写测试之前弄清楚用户将如何使用软件(要么通过试验性代码,要么同一名设计师一起工作)。一旦弄清楚了,就可以开始工作了。\

在理想情况下,你将为用户体验的某个部分创建端到端的测试,并编写代码让其通过测试。在编写那些代码的时候,你会创建单元测试,具体化需要创建或修改(通常是后者)的代码的规范。\

问题是,编写没有用户界面工件(HTML)可供参考的、端到端的失败测试很难。这是因为,大部分端到端测试的形式都是:\

  1. 找到页面上的某个元素; \
  2. 通过某种方式同它交互; \
  3. 证实交互成功; \
  4. 重复上述过程直到测试结束。

这意味着,围绕要发生交互的用户界面元素(DOM对象),你需要有一些规范。当把以JavaScript为基础的交互设计考虑在内时,如果不实际地构建界面,至少是部分地构建,就更难测试了。\

为此,要让一个粗略的UI轮廓在浏览器中运行起来。使用预先准备好的数据,并且不需要考虑备选流程——一次专注于一件事。它运行起来以后,就可以编写测试了。\

在这样做的时候,有两点需要考虑:这个特性需要测试吗?如果需要,该如何测试?\

测试什么

\

虽然在编程上没有愉快路径,但用户经历的代码路径要比代码的可能路径少许多。例如,当用户购买一款产品,根据用户地址、选择的发货方式或者以前的购买历史,我们可能会用不同的方式处理订单。在所有情况下,用户的体验都是一样的,这样,在用户看来,流程只有一个。\

这时,你的目标是测试所有的用户流程。你需要一个测试套件,模拟一个用户做你想要并希望他做的事,并断言你想要提供给该用户的所有体验都工作正常。\

假如你已经知道要测试什么,那应该如何进行呢?\

如何进行端到端测试

\

如果修改了一个流程,那么就要修改那个流程的测试。由于端到端测试模拟用户活动,所以不需要为想要断言的每件事情都编写一个测试。如果用户应该在结算界面上看到三段重要的信息,就不需要编写三个测试——一个测试检查所有三段信息就足够了。因此,当修改一个现有的用户体验时,要找一个现有的、可以改进的测试。\

否则,就需要一个新的测试。记住,你的目标是模拟用户要做的事情。务必要对如何组织测试中的导航和行为开诚布公。用户真地会直接导航到某些深层链接吗?或者他们会点击某个公用的开始页面从而到达他们需要到达的地方吗?\

这很难做,尤其是通常要使用最少的标记实现该功能。测试需要定位特定的DOM元素同其交互,而准确找到你想要同其交互的元素并不总是很简单(或者可能)。你需要“标识(signpost)”。\

标识是专门插入DOM中用于定位感兴趣的元素的。要尽早确定这些标识如何发挥作用。不应该使用原本用于样式化的CSS类来定位DOM元素。这样做意味着前端开发人员改变类名就会破坏测试。也不应该使用被JavaScript代码使用的CSS类或数据属性(比如前缀为js-的类)。这会带来同样的破坏。\

使用前缀为test-的CSS类或者前缀为data-test-的属性是两种常用的技术:

\u0026lt;section class=\"component dark test-checkout-confirmation\"\u0026gt;\  \u0026lt;!-- ... --\u0026gt;\\u0026lt;/section\u0026gt;\\\u0026lt;!-- 或者 --\u0026gt;\\\u0026lt;section class=\"component dark\" data-test-checkout-confirmation\u0026gt;\  \u0026lt;!-- ... --\u0026gt;\\u0026lt;/section\u0026gt;\
\

这可能看上去让人不舒服……也确实是。但是,与将测试耦合到内容或者展示类相比,这就不那么令人讨厌了。这里,你需要寻求一种平衡——不要盲目地使用data-test属性标记每个元素。例如,如果你想点击一个购买特定产品的按钮,那么你真正需要的只是定位某个包含那款产品及购买按钮的元素。

\u0026lt;article data-test-product=\"1234\"\u0026gt;\  \u0026lt;!-- a ton of markup --\u0026gt;\  \u0026lt;input type=\"submit\" name=\"Purchase\" value=\"Purchase\"\u0026gt;\\u0026lt;/article\u0026gt;\\u0026lt;article data-test-product=\"5678\"\u0026gt;\  \u0026lt;!-- a ton of markup --\u0026gt;\  \u0026lt;input type=\"submit\" name=\"Purchase\" value=\"Purchase\"\u0026gt;\\u0026lt;/article\u0026gt;\
\

添加data-test-product属性后,你就能够使用一个像[data-test-product='1234'] input[type='submit']这样的CSS选择器定位产品1234的购买按钮了。\

这意味着你必须修改只为测试而存在的标记,就是说,为了获得你提供给他们的用户体验,用户要下载一些他们不需要的字节。这是一种平衡,但比糟糕的测试覆盖率(对用户的伤害远远超过了HTML中多一些额外的字节)要好。只是得恰到好处。\

当页面上有改变页面内容而又不重新加载的交互(换句话说,使用JavaScript)时,这项技术就更加重要了。\

处理交互

\

当每次点击都重新加载页面时,端到端测试更可靠,因为底层工具知道要等待一个页面重新加载。当用户交互只是改变DOM时,难度就大了,因为工具不知道什么“事情”正在发生,也就无法“等待事情完成”。\

当测试需要同一个不会根据用户动作重新加载的页面交互时,就需要一种方法能够在开始断言发生了什么之前等待DOM操作完成。如果不等待,那么如果测试开始断言时DOM还没有更新,测试就会无谓地失败。\

就像在标记中使用标识定位要操作的DOM元素一样,我们也可以把它们用在这里。任何新增或变化的标记都应该有某种在交互失败或没有发生的情况下不会出现的标识。换句话说,你不必为了等待DOM事件而在测试中进行休眠调用——DOM中应该包含可供测试显式等待的标识。\

例如,假设我们想要测试一个动作为用户生成了一条成功的消息。假设实现方法是发出一个AJAX请求,当调用结束时向DOM中插入一条消息。一个基本的实现可以像下面这样做:

function purchase(productId) {\  $.post(\        \"/products/\

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/398214.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux 命令/目录 名称 英文单词 缩写 助记

注:以下内容转自https://www.zhihu.com/question/49073893?sortcreated,作者是一个高中的学生,能总结这么多这么好,真心赞扬。这么好的知识我怕在互联网上流失,所以在这里做了一个备份。 以下内容仅供参考&#xff0c…

python文件处理seek()方法的参数是_Python 文件(File) seek() 方法

例如:将当前文件位置更改为4,然后返回其余行:f open("demofile.txt", "r")f.seek(4)print(f.readline())1、定义和用法seek()方法设置文件流中的当前文件位置。seek()方法如果操作成功,则返回新的文件位置&a…

一天一个类,一点也不累之HashSet

最近忙着一个小项目结题,故没能按时完成【一天一个类,一点也不累】,还好项目优秀,算是对自己一点点的安慰和鼓励。~~~ 今天要说的是HashSet 既然是继承自Set,那么就必须有Set的一些属性,比如不能容许有相同…

[BZOJ1502]月下柠檬树(自适应辛普森积分)

1502: [NOI2005]月下柠檬树 Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 1387 Solved: 739[Submit][Status][Discuss]Description 李哲非常非常喜欢柠檬树,特别是在静静的夜晚,当天空中有一弯明月温柔地照亮地面上的景物时,他必会悠闲地…

擎天出口退税软件_新版申报系统退税申报注意事项

前言:随着税务信息化建设“后金三”时代的不断深入,2019年年底国家税务总局启动出口退税管理系统整合(进入金税三期)项目,经过广东、大连两个地区试点,即将于今年年底前在全国完成金税三期审核系统的上线工作,至此,出口…

词性标注

4.10 词性标注 词性用来描写叙述一个词在上下文中的作用。比如描写叙述一个概念的词叫做名词,在下文引用这个名词的词叫做代词。有的词性常常会出现一些新的词,比如名词,这种词性叫做开放式词性。另外一些词性中的词比較固定,比如…

MVCC浅析(转)

在并发读写数据库时,读操作可能会不一致的数据(脏读)。为了避免这种情况,需要实现数据库的并发访问控制,最简单的方式就是加锁访问。由于,加锁会将读写操作串行化,所以不会出现不一致的状态。但…

关于单链表,二叉树,图,查找和排序的软件编程

课程名称:计算机软件 使用软件:devcpp 注意:这里列出了关于单链表,二叉树,图,查找和排序的编程,全部程序由博主一人编写,会有瑕疵,谨慎使用。 1.单链表 要求:…

pcie 的function_PCIe扫盲——BDF与配置空间

前面的文章中介绍过,每一个PCIe设备可以只有一个功能(Function),即Fun0。也可以拥有最多8个功能,即多功能设备(Multi-Fun)。不管这个PCIe设备拥有多少个功能,其每一个功能都有一个唯一独立的配置空间(Configuration Space)与之对应…

微信二维码扫描下载APK

前几天给客户制作的app需要上线,生成二维码扫描进行下载,把生成好的apk挂在服务器端,将地址复制下来,通过草料二维码(http://cli.im/)生成一个二维码它需要一个应用宝ID。上传到应用宝,光审批就…

mysql 字段 as_mysql 字段as详解及实例代码

mysql 字段使用as在mysql中,select查询可以使用AS关键字为查询的字段起一个别名,该别名用作表达式的列名,并且别名可以在GROUP BY,ORDER BY或HAVING等语句中使用。例如:SELECT CONCAT(last_name,, ,first_name) AS ful…

dubbo接口快速测试技巧

在分布式系统的开发中,用到了dubbozookeeper技术,最近遇到一个问题,产品上线后,我负责的模块出了问题,某个bean中某个字段的值一直为null,而这个bean是我调用注册在zookeeper上的一个服务查询到的&#xff…

跳出多重循环 JS

关于如何跳出多重循环loop: //循环标记for(var key in jsonObj){for(var i0;i<jsonObj[key].length;i){if(jsonObj[key][i].password123456){break loop;//跳出双重循环 }}}转载于:https://blog.51cto.com/9381188/1790457

webservice引用spring的bean

1 <jaxws:endpoint address"/test/webservice" implementor"#testBean" /> 这行代码里面的#号表示webservice自动装配到spring的bean。转载于:https://www.cnblogs.com/xiluhua/p/4472544.html

mysql 5.0 数据库_mysql5.0常用命令

MySQL常用操作基本操作&#xff0c;以下都是MySQL5.0下测试通过首先说明下&#xff0c;记住在每个命令结束时加上&#xff1b;(分号)1.导出整个数据库mysqldump -u 用户名 -p --default-character-setlatin1 数据库名 > 导出的文件名(数据库默认编码是latin1)mysqldump -u w…

IISASP.NET 站点IP跳转到域名

前言&#xff1a;先到微软的 https://www.iis.net/downloads/microsoft/url-rewrite 下载URL Rewrite 目标&#xff1a;输入ip跳转到域名所在的网站 比如58的115.159.231.173 跳转到https://passport.58.com/login 先看下58的例子 我们在地址栏输入ip之后 箭头指向的地方是跳转…

java基础五

继承 1. 继承含义 在面向对象编程中&#xff0c;可以通过扩展一个已有的类&#xff0c;并继承该类的属性和行为&#xff0c;来创建一个新的类&#xff0c;这种方式称为继承&#xff08;inheritance&#xff09;. 2. 继承的优点 A&#xff0e;代码的可重用性 B&#xff0e;子类可…

Mac OS X 下Java开发环境配置

首先自己又装的jdk1.8版本目录&#xff1a;/Library/Java/JavaVirtualMachines/jdk1.8.0_91.jdk/Contents/Home待添加。。。转载于:https://blog.51cto.com/jiyanle/1790537

mysql emoji表情_让MySQL支持Emoji表情 mysql 5.6

最近在做微信相关的项目&#xff0c;其中MySQL 要存储emoji表情&#xff0c;因此发现我们常用的utf8 字符集根本无法存储表情。网上有不少替代方案。本人还是采用了修改MySQL字符集的方案简单快捷。首先将我们数据库默认字符集由utf8 更改为utf8mb4&#xff0c;对应的表默认字符…

高效开发iOS系列 -- 那些不为人知的KVC

我的简书地址&#xff1a;http://www.jianshu.com/p/a6a0abac1c4a valueForKeyPath 本篇来解说一下那些不为人知&#xff0c;也常常被忽略掉&#xff0c;而且非常有用的KVC干货小技巧 获取数组里的,最大、最小、平均、求和 NSArray *array ["1","3",2,9.5…