1 概念
1.1 单元测试
Search in Wikipedia: In computer programming, unit testing is a method by which individual units of source code, sets of one or more computer program modules together with associated control data, usage procedures, and operating procedures are tested to determine if they are fit for use.
高级人话:
在计算机程序设计中,单元测试是一种方法,通过对每个单元的源代码、一个或多个计算机程序模块的设置、相关的控制数据、使用程序和操作程序进行测试,以确定它们是否适合使用。
一句话:
每个单元测试就是一段用于测试一个模块或接口是否能达到预期结果的代码。
1.2 测试驱动开发(Test Driven Development)
简称TDD,具有很强的目的性,在直接结果的指导下开发生产代码,然后不断围绕这个目标去改进代码,其优势是高效和去冗余的。所以其特点应该是由需求得出测试,由测试代码得出生产代码。
1.3 行为驱动开发(Behaviour Driven Development)
简称BDD,TDD的二代,主观的区别就是用词,‘example’取代了‘test’,‘describe’取代了‘class’,‘behaviour’取代了‘method’等等。这正是其特征之一,自然语言的加入,使得非程序人员也能参与到测试用例的编写中来,也大大降低了客户、用户、项目管理者与开发者之间来回翻译的成本。
2 为什么做单元测试
2.1 正确性
测试不可能保证一个程序是完全正确的,但是测试却可以增强我们对程序完整的信心,测试可以让我们相信程序做了我么期望它做的事情。测试能够使我们尽早的发现程序的 bug 和不足。
2.2 自动化
当然手工也可以测试,通过console可以打印出内部信息,但是这是一次性的事情,下次测试还需要从头来过,效率不能得到保证。通过编写测试用例,可以做到一次编写,多次运行
2.3 解释性
测试用例用于测试接口、模块的重要性,那么在测试用例中就会涉及如何使用这些API。其他开发人员如果要使用这些API,那阅读测试用例是一种很好地途径,有时比文档说明更清晰
2.4 驱动开发,指导设计
代码被测试的前提是代码本身的可测试性,那么要保证代码的可测试性,就需要在开发中注意API的设计,TDD将测试前移就是起到这么一个作用
2.5 保证重构
随着项目的迭代,函数、方法、变量都在递增,尤其是进度的不足,来自产品经理的压力,还有QA所带来的各种Bug报告会让原本整洁的代码变得一片混乱。这时就最好进行代码重构,那怎么才能保证重构后代码的质量呢?单元测试就成了一个标准,确保重构后不会改变结果。
2.6 测试覆盖率
直接在测试环境中使用软件会伴随着一大批的附带操作大量增加测试时间,并且无法保证其测试覆盖率。所以单元测试的目的是更高效更稳定的确认其是否可用。
2.7 降低BUG代价
一个 bug 被隐藏的时间越长,修复这个 bug 的代价就越大。在《快速软件开发》一书中已引用了大量的研究数据指出:最后才修改一个 bug 的代价是在 bug 产生时修改它的代价的10倍。
3 问题&误区
3.1 不知道怎么编写单元测试
这个问题在于,还没有接触过单元测试,同时,也没有体会过企业级的代码开发。实际开发中,调试BUG,定位问题常常是比较耗时的,而如果已经做了单元测试,并确定了关键点的正确值,对于BUG的定位将更快。
3.2 项目没有要求,所以不编写
这个反映出了一种现象,同时也是一种习惯。项目有没有要求,只能说明项目的管理上不严格或者规范不到位,并不是程序员不编写单元测试的理由。
3.3 单元测试,甚至整个测试都是在编码结束后的一道工序
测试应该伴随整个编码或软件周期进行,例如TDD指明单元测试将超前于编码。单元测试应该是一个框架、标准,经常被形容被脚手架,像建筑一样,脚手架的高度至少应该和大楼高度不相上下,甚至一开始就搭好脚手架。
3.4 单元测试价值不高,完全是浪费时间
这种说法其实是错误的。为什么这么说呢?在日常的开发中,代码的完工其实并不等于开发的完工。如果没有单元测试,那么如何保证代码能够正常运行呢?测试人员做的只是业务上的集成测试,也就是黑盒测试,对单个的方法是没有办法测试的,而且,测试出的 bug 的范围也会很广,根本不能确定 bug 的范围,还得去花时间来确定 bug 出在什么地方。难道这就不浪费时间了吗?甚至,这样的方式,时间浪费的会更多。
3.5 生成代码变更,单元测试也得跟着变
如果是需求上的变更确实如此,否则单元测试是为了生产代码而写,它应当足够的自由奔放,去适应各种各样的生产代码。
3.6 业务逻辑比较简单,不值得编写单元测试
所谓的业务逻辑比较简单,其实是相对的。当你对某一块业务逻辑很熟悉的时候,你自然会认为它很简单。然而,单元测试的必要性并不是仅仅在于测试代码的功能是否正确,还在于,当其他同事在了解你的业务的时候,能够很快的通过单元测试来熟悉代码的功能,甚至不用去读代码,就能够知道它做了哪些事情。因此,写单元测试不仅是解放了自己,更方便了别人。
3.7 项目前期还在尽量写测试,到了后期就失控了
这种问题的原因在于,对项目进度、项目中的技术点研究时间、人员的沟通、业务需求的熟悉程度等没有把控好。这个问题的出现并不是个人的问题,而是反映了项目管理中存在的问题。当然,个人的原因也存在,就是如何在有限的时间里,提高效率。
3.8 为了完成编码任务,没有足够的时间编写单元测试
这个问题在于,程序员领取的任务较为复杂,或者自己的开发效率有待提高。其实,开发任务是包括编码和单元测试的。在领任务的时候,应该跟据自身的能力,跟组长或经理沟通好,以便留出一定的测试时间。当然,提高自己的编码效率也是很有必要的。
3.9 单元测试必须是一个runner集中运行所有单元的测试,并一目了然
不,这仅仅是一种自动化单元测试的最佳实践,在一些小型项目中单元测试可能仅仅是一组去除其他特性的接口调用。
3.10 单元测试中只是一个红或绿的条目
在一些图形处理或布局的项目中单元测试可以结合自身特性变的十分有趣,比如Masonry网格布局库,一行一行的小格布局用以说明布局被完成的事实,这样比代码检查布局是否正确再以颜色显示结果来得更直观高效,也避免了测试程序本身的bug导致的失误。
3.11 何时提交代码
在开发中,对于自己开发的模块,只有在通过单元测试之后,才能提交到 SVN 库 或者 Git 库。
4 怎么做单元测试
4.1 自动化Runner框架
- Karma:Google的新贵Karma出现了,它使用Nodejs构建,因此跨平台,还支持PhantomJS浏览器,还支持多种框架,包括以上介绍的Jasmine、Qunit和Mocha。一次可以在多个浏览器及设备中进行测试,并控制浏览器行为和测试报告。
Karma官网
karma+webpack搭建vue单元测试环境_Github —— 同样适用于AngularJs的Karma配置
如果出现Karma插件的配置问题可以参考Karma官网_Plugin - TestSwarm:测试环境由服务器提供
- Buster:和Karma很像
- JSTD(Javascript Test Driver):已淘汰
4.2 代码质量平台
Sorna官网
Sorna项目中配置
其他配置项示例
|
|
设置环境变量
|
|
4.3 单元测试框架
4.3.1 测试框架
判断内部是否存在异常,存在则console出对应的text信息
- Jasmine: Behavior-Drive development(BDD)风格的测试框架,在业内较为流行,功能很全面,自带asssert、mock功能
- Mocha: node社区大神tj的作品,可以在node和browser端使用,具有很强的灵活性,可以选择自己喜欢的断言库,选择测试结果的report
- Intern: 看官方介绍该测试框架功能极其全面,似乎囊括了业内跟测试相关的所有功能
- Jest:Jest是由Facebook发布的开源的、基于Jasmine的JavaScript单元测试框架。Jest的目标是减少开始测试一个项目所要花费的时间和认知负荷,因此它提供了大部分你需要的现成工具:快速的命令行接口、Mock工具集以及它的自动模块Mock系统。
- Qunit: 该框架诞生之初是为了jquery的单元测试,后来独立出来不再依赖于jquery本身,但是其身上还是脱离不开jquery的影子
4.3.2 断言库
当actual值与expect值不一样时,就抛出异常,供外部测试框架检测到,这就是为什么有些测试框架可以自由选择断言库的原因,只要可以抛出异常,外部测试框架就可以工作。
- Chai:应该是目前组流行的断言库了,支持TDD(assert)、BDD(expect、should)两个风格的断言库
- Should.js: TJ的另外一个开源贡献
- Expect.js:BDD风格的另外一个断言库,基于should.js,是mini版的BDD库
- Assert(node自带核心模块): 可以在node中使用的断言模块
4.3.3 Mock库
需要测试的单元依赖于外部的模块,不适合直接使用依赖的模块,这样情况下就需要对其进行mock,也就是伪造依赖的模块。创建一个新的函数,用这个函数来取代原来的函数,同时在这个新函数上添加一些额外的属性,例如called、calledWithArguments等信息
- Sinon.js: 目前使用最多的mock库,将其分为spies、stub、fake XMLHttpRequest、Fake server、Fake time几种,根据不同的场景进行选择。
4.4 原则
- 测试代码时,只考虑测试,不考虑内部实现
- 数据尽量模拟现实,越靠近现实越好
- 充分考虑数据的边界条件
- 对重点、复杂、核心代码,重点测试
- 利用AOP(beforeEach、afterEach),减少测试代码数量,避免无用功能
- 测试、功能开发相结合,有利于设计和代码重构
4.4 E2E 模拟用户行为测试
E2E(End to End)测试:把整个系统当作一个黑盒,测试模拟真实用户在浏览器中操作UI,测试出的问题,可能是前端也可能是后端导致的。因此,E2E测试一般是由测试工程师来做。主要目的:便于给PM展示业务流程、便于修改Bug之后的回归。
4.4.1 Selenium: 测试工具集
Selenium 分为两部分:
- Selenium IDE是一个Firefox浏览器的插件,可以录制用户行为,并快速测试。
- Selenium WebDriver是一个多语言的驱动浏览器的工具,支持Python、Java、Ruby、Perl、PHP或.Net。并且可以操作IE、Firefox、Safari和Chrome等主流浏览器。通过 open , type , click , waitForxxx 等指令来模拟用户行为,比如用Java测试:
|
|
首先跳转到跟目录,然后选择类型,点击按钮G,并等待页面载入30秒,然后使用断言测试。
4.4.2 WATIR: 测试工具
WATIR 是Web Application Testing in Ruby的缩写,显然它只支持Ruby语言来操作浏览器模拟用户行为。官方声称它是一个简单而灵活的工具,无论怎样至少就官方网站的设计来看要比Selenium简约多了。同样支持模拟链接点击,按钮点击,还有表单的填写等行为。不过WATIR不支持Ajax的测试。
|
|
这样就使用watir完成了一次表单填写。
4.4.3 Nightwatch: JavaScript Webpack Template测试框架
Nightwatch: Nightwatchjs is a UI automated testing framework powered by Node.js. It uses the Selenium WebDriver API. ( Ps:懒得翻译了,看不懂我就鄙视你了!)是的,没错需要Selenium WebDriver的支持。好处就是用JavaScript代码模拟操作,支持Webpack。
原理图:
代码示例:
|
|
说明:在VUE项目初始化时,通过vue-cli除了可以配置单元测试,同样可以配置Nightwatch。
如果E2E测试代码需要与项目源代码分开,可以参考此项目:
Aniber–是由Selenium和Nightwatch提供支持的e2e测试工具。它是独立的,你可以测试任何你想要的任何网站。
4.4.4 Protractor
- Protractor是作为针对Angular JS应用程序的测试框架。
- 提供了一些新的定位策略及功能,来更好的支持Angular JS。
- 构建基于Selenium WebDriver之上,且围绕着Selenium WebDriver进行封装。
|
|
4.5 单元测试API用例
见 前端单元测试详解(二)__单元测试实例:JavaScript VUE/AngularJs & Jasmine
5 参考文章
关于前端开发谈谈单元测试_推荐(含持续集成测试,例 )
谈谈单元测试之(一):为什么要进行烦人的单元测试?
前端单元测试总结
五个值得尝试的前端开发工具
karma+mocha+webpack3 搭建 vue2 单元测试环境
VUE官网单元测试说明
Vue单元测试起步_Karma插件说明可以看这里
Vue单元测试case写法
前端测试框架Jest系列教程
怎么为大中型的vue.js项目编写e2e测试
E2E testing with Nightwatch.js
使用Nightwatch.js做基于浏览器的web应用自动测试
Webpack单元测试,e2e测试
vue-cli 如何去掉单元测试
安装protractor进行前端自动化测试-web ui自动化测试
使用Nodejs+Protractor搭建测试环境+Protractor的安装及其遇到的问题
最受欢迎的 5 款 Node.js 端到端测试框架