前端架构设计3:测试核心
测试核心
(一) 传统手工测试的局限性
软件测试是在规定的条件下对程序进行操作, 以发现程序中的错误, 衡量软件质量, 并对其是否能满足设计要求进行评估的过程, 软件测试的目的是希望以最小的代价尽可能多地找出软件中潜在的错误和缺陷.
首先,测试人员会针对开发人员开发的功能写出测试用例, 例如表单应该填入的数据, 页面单击顺序, 以及最后页面期待的效果, 然后, 测试人员会按照用例一步步进行手工校验, 如果页面行为异常, 例如无法打开页面或生成的数据不准确, 则会在企业缺陷管理系统中提交缺陷记录, 供开发人员进行修正. 在开发过程中, 如果有新版本编译出来, 测试人员需要根据测试用例重新测试, 确认是否有新缺陷, 或者老缺陷是否已经得到了修正.
长久以来, 这种传统手工测试在各大公司广泛应用, 并已被证明能够行知有效的保证产品质量, 但伴随着互联网技术的发展, 这种传统的测试模式已经显示出越来越多的瓶颈.
1. 重复性工作, 测试质量低
现在的互联网产品开发研究的是短平快, 小步快走, 短则两三天, 长则一星期就会发布新版本. 在这短短的时间里, 测试人员需要把新版本部署到测试环境, 更新数据库 然后对所有的测试用例进行手工校验. 这个过程事件紧迫, 工作量大, 而且具有很高的机械性和重复性, 当测试人员长期工作在重复性的验证事物上, 往往会因为思维习惯而忽略新出现的问题, 最后导致不仅测试人员自身缺乏工作热情, 而且测试质量更难以保证.
2. 测试效率低
手工测试天生就决定了它的执行效率很低, 测试人员需要根据测试用例逐行逐字阅读, 然后在页面上一步步填写表单, 在单击按钮提交, 这是一个非常繁琐的过程. 而遇到复杂的业务流程更是涉及方方面面, 作者甚至见过一个多小时都无法完成的测试案例. 到了开发后期, 可能每天或每两天就要发布一个版本进行测试, 如果一个软件系统的功能点有几千甚至上万个, 手工测试将特别耗时和繁琐, 不仅消耗了大量的人力, 还可能影响到产品的如期发布.
3. 无法保证覆盖代码全路径
是否有良好的测试覆盖是考核测试成熟度的重要指标, 其核心思想是对相同的业务逻辑提供多组甚至几十组输入, 全面覆盖到业务中的大多数路径, 重点考察软件的边界行为. 比如某个页面输入框的字符个数在开发中被限制为256个字符, 但测试人员很可能漏掉这样的极端输入情况. 由于手工测试效率很低, 不要说进行几十组数据的测试, 就是几组可能都难以实施. 另外, 有些软件缺陷需要在大量数据或者大量并发用户的情况下才会暴露, 很难通过手工测试保证代码的全路径覆盖.
4. 无法有效兼顾多浏览器, 多平台
Web前端的测试环境复杂, 兼容性要求高, 特别是要同时兼顾多种操作系统, 包括Window, Mac OS和Linux, 以及不同的浏览器IE, Edege, Chrome, Safari等, 还要考虑移动端的IOS, Andorid等操作系统, 其排列组合之后将会是通过手工测试无法企及的数字. 很难想象有那个公司能够投入巨大的人力成本完成如此庞大的手工测试.
(二)前端测试的分类
1. 单元测试(Unit Test)
在软件开发过程中, 最基本的测试就是单元测试, 这是针对程序单元(软件设计的最小单位)来正确性检验的测试工作. 程序单元是应用的最小可测试部件. 在过程化编程中, 一个单元就是单个程序, 函数, 过程等; 对于面向对象编程, 最小单元就是方法. 在企业的质量控制体系中, 单元测试是由开发部门在软件提交测试部门前完成.
单元测试的目标是打破程序单元间的依赖关系, 隔离单元并证明这些单个单元是正确的, 所以单元测试应该无依赖和隔离, 通常在单元测试中, 把系统的依赖组件提取出来, 用测试替身(Test Double)取而代之, 把单元测试把注意力集中放在测试"单元"的逻辑上面而不是和第三方系统的交互上.
2. 集成测试(Integration Test)
即使一个程序在单元测试中运作良好, 也不能确定他们放在一起能正常工作, 集成测试是取出应用程序里可以独立运行的组件, 通常是一些单元的集合, 来测试这部分单元作为一个整体的表现, 以验证他们能否协调一致的运作. 集成测试一般位于单元测试之后 端到端测试之前.
例如一个常见的集成测试场景是使用数据组件对数据库进行操作的测试. 测试人员需要安装并配置好数据库, 然后在数据库里插入预先准备好的数据, 再执行需要测试的组件, 运行完毕后检验数据库里的数据. 在这个测试场景中, 被测单元依赖数据库访问模块, 所以它不是一个单元测试. 但是它也没有模拟一个完整的用户真实场景, 所以它也不是一个端到端的测试.
3. 端到端测试(End-to-End Test)
端到端测试(通常缩写为E2E)是把产品或服务当做一个整体进行验证, 典型的做法是模拟真实的用户场景, 通过与系统的需求定义做比较, 来发现产品和需求定义不符合或存在矛盾的地方, 其最终目的是为了发布产品. 例如在Web应用程序中, 测试人员会启动服务器, 打开浏览器, 访问被测网页, 并操作网页上需要测试的功能, 检查浏览器中发生的特定的事件, 以确保被测功能可以正常运行.
端到端测试通常由测试部门完成, 一般有以下特性:
- 需要搭建专门的测试环境模拟真实的用户场景, 成本较高
- 测试用例复杂, 运行时间长
- 一旦测试发现问题, 由于涉及的模块比较多, 定位问题难度较高
端到端测试可以手工完成, 也可以变现测试框架和测试代码自动执行. 在Web前端应用中, 端到端测试通常从用户界面开始, 核实用户与应用之间的交互, 确保用户界面向用户提供了适当的访问测试对象功能的操作, 同时还要确保内部的对象符合预期要求. 如果进行手工测试的话, 效率低下, 无法满足快速迭代的Web前端应用的测试需求, 所以迫切需要将Web前端应用的端到端测试自动化.
(三)测试驱动开发的理念(TDD: Test-Driven Development)
1. TDD的优势
TDD的基本思路就是通过测试来推动整个开发的进行。而测试驱动开发技术并不只是单纯的测试工作。
需求向来就是软件开发过程中感觉最不好明确描述、易变的东西。这里说的需求不只是指用户的需求,还包括对代码的使用需求。很多开发人员最害怕的就是后期还要修改某个类或者函数的接口进行修改或者扩展,为什么会发生这样的事情就是因为这部分代码的使用需求没有很好的描述。测试驱动开发就是通过编写测试用例,先考虑代码的使用需求(包括功能、过程、接口等),而且这个描述是无二义的,可执行验证的。
通过编写这部分代码的测试用例,对其功能的分解、使用过程、接口都进行了设计。而且这种从使用角度对代码的设计通常更符合后期开发的需求。可测试的要求,对代码的内聚性的提高和复用都非常有益。因此测试驱动开发也是一种代码设计的过程。
开发人员通常对编写文档非常厌烦,但要使用、理解别人的代码时通常又希望能有文档进行指导。而测试驱动开发过程中产生的测试用例代码就是对代码的最好的解释。
快乐工作的基础就是对自己有信心,对自己的工作成果有信心。当前很多开发人员却经常在担心:“代码是否正确?”“辛苦编写的代码还有没有严重bug?”“修改的新代码对其他部分有没有影响?”。这种担心甚至导致某些代码应该修改却不敢修改的地步。测试驱动开发提供的测试集就可以作为你信心的来源。
当然测试驱动开发最重要的功能还在于保障代码的正确性,能够迅速发现、定位bug。而迅速发现、定位bug是很多开发人员的梦想。针对关键代码的测试集,以及不断完善的测试用例,为迅速发现、定位bug提供了条件。
2. TDD的原理
测试驱动开发的基本思想就是在开发功能代码之前,先编写测试代码。也就是说在明确要开发某个功能后,首先思考如何对这个功能进行测试,并完成测试代码的编写,然后编写相关的代码满足这些测试用例。然后循环进行添加其他功能,直到完全部功能的开发。
3. TDD的过程
测试驱动开发的基本过程如下:
- 明确当前要完成的功能。可以记录成一个 TODO 列表。
- 快速完成针对此功能的测试用例编写。
- 测试代码编译不通过。
- 编写对应的功能代码。
- 测试通过。
- 对代码进行重构,并保证测试通过。
- 循环完成所有功能的开发。
(四) 测试工具推荐
1. Jasmine
Jasmine应该算是最成熟的Javascript测试框架,它自带断言和测试执行环境, 并有拥有灵巧而明确的语法可以让你轻松的编写测试代码。
2. Mocha
Mocha同样也是一个前端框架, 它上手简单且有丰富的插件.如果想学习的, 可以看一下阮一峰的教程:测试框架 Mocha 实例教程
3. Karma
Karma是由Google团队开发的一个测试工具, 它不是一个测试框架, 只是一个跑测试的驱动. 你可以通过karma的配置文件集合你喜欢的框架, 断言库和浏览器.