前端的测试工具
背景
在看Angular的测试时,发现了许多新的术语 jasmine
, karma
, e2e
, spy
等等,
认为非常有必要先了解一下前端测试的概念和工具集.
测试原因
近年来出现了许多前端框架,SPA渐渐兴盛,
许多原本由后端进行的工作被带到了前端进行.
相应功能和函数的数量越来越多,复杂性越来越高.
自然地,对一些功能的测试就必不可少.
测试分类
前端测试也分许多种类
- 单元测试
- 集成测试
- UI测试
- 端到端测试
其中单元测试的代码通常和功能代码放在相同路径下(至少angular是这样的),
这样做有许多好处
- 易于找到,且能发现哪些功能还没有测试
- 移动或重命名时不会忘记测试代码
端到端(e2e)测试是站在用户角度的测试,属于黑盒测试.
如果有这么一个工具进行e2e测试,那么它至少需要能够模拟出浏览器中的操作.
而测试用的代码,由于通常是跨文件跨功能的测试,
因此不选择和特定功能放在一起,而是单独放一个文件夹,比如 e2e
测试框架
前端框架通常需要提供的功能有
- 断言功能:对比函数的实际输出和期望输出(核心功能)
- 对测试用例的结构进行管理(比如建立测试集的概念,方便代码的组织)
- 管理测试用代码在运行时的一些工作
- 公共变量的赋值
- 依赖注入
- 仿真(模拟一个返回值,方法,模块,甚至服务器)
- 生成,展示测试结果
- 调用浏览器或模拟浏览器等等
事实上直接沿用许多后端测试框架的概念就可以了,顶多多加一个浏览器的支持
前端框架也有不少
- Jasmine Angular建议的一个测试框架,辈分奇高.
- Mocha 曾经用的人多.
- Jest 受到Jasmine启发而开发的框架,由Facebook团队开发和维护,目前最受欢迎.
- Tape 体积小,只提供最关键的东西
jasmine
一个前端测试框架,特点有
- 不依赖其他js框架
- 不使用DOM(TODO意味着什么?)
- 开箱即用(自带断言和仿真功能)
- 可以运行在html或node.js中
- 基于BDD(行为驱动开发 TODO 和TDD的具体区别?)
基础念概
- Suite 测试集(测试用例的集合)
用describe
声明
可嵌套 - Spec 测试用例,用
it
声明 - Expectation 断言,用于比较真实值和期望值,得出测试结果
用expect
关键字 - Matcher 匹配方法,是具体执行比较的函数,参数是期望值
比如toBe
,toEqual
,toMatch
,
可以用not
修饰,比如not.toBe(null)
, 现在可能用toNotBe(null)
来代替了? - Setup与Teardown 搭建与拆除,每个测试用例运行前后,可以统一运行一些步骤
比如每个用例里都要用到一些变量,则可以- 在
describe
里声明变量 - 在
beforeEach
是给变量赋值 - 在
afterEach
中收拾残局
- 在
- 跳过测试,可以使用
xdescribe
与xit
使用举例
1 | describe("A test suite for SchoolService", function() { // 声明一个测试集 |
高级用法
- 元数据测试
- Timeout测试
- 异步调用测试
- spy概念
-
spy相关
将一个原有的部件,替换成听取测试框架指使的spy.以方便测试.
比如可以- 捏造返回值
- 监控被调用次数
使用案例
1
2
3
4
5
6
7
8
9// 做好spy
let serviceMethodSpy = spyOn(service, 'method');
// 捏造返回值,在method被调用时,即可返回此处设置的值
serviceMethodSpy.and.returnValue(1);
// 监控被调用次数
expect(serviceMethodSpy).toHaveBeenCalled();
expect(serviceMethodSpy).toHaveBeenCalledTimes(1);另外也可以选择不保留
spyOn
的返回值,直接让method所属的service变成一个SpyObj
.
通常的原因是- 为了使用方便
- serivce还没有写好,但现在要用
1
2
3
4
5const service: jasmine.SpyObj<Service> = jasmine.createSpyObj('Service', ['method']);
// 捏造返回值
service.method.and.returnValue(1);
// 监控被调用次数
expect(service.method).toHaveBeenCalled(); -
支持异步调用的测试
web中有许多异步调用,那测试框架也需要能够支持这些异步代码的结果测试.
正常想法,完全可以在subscribe
中来进行断言操作,事实上也的确可以.jasmine可以三种方法来进行测试
- 让回调真的执行,不过要等待所有回调结束
- 使用一个假的模拟时间流逝的函数等待回调结束
- 对于非Observable或Promise的异步行为(比如js原生的setTimeout等),提供另外的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25// 方法一
it('should be get user list (async)', async(() => {
// 调用被测试的方法
fixture.whenStable().then(() => { // whenStable等待所有回调完毕
fixture.detectChanges();
expect(true).toBe(true);
});
}));
// 方法二
it('should be get user list (async)', fakeAsync(() => {
// 调用被测试的方法
tick(); // 模拟时间流逝,不再使用then,减少一层嵌套
fixture.detectChanges();
expect(true).toBe(true);
}));
// 方法三
it('async demo', (done: () => void) => {
context.show().subscribe(res => {
expect(true).toBe(true);
done(); // 第三种方法的核心? TODO
});
el.querySelected('xxx').click();
});
karma工具
这些测试代码是js,它们需要一个能够运行的环境,
比如一个web页面,或者是一个node.js环境,
这个页面称为runner.
土一点的方法,甚至新建一个html文件,然后直接在script里写都行.
1 |
|
高级一些的karma也提供这样的一个环境,
除了能够调用各种浏览器,
也可以基于node.js运行,因此能够在CI环境中使用.
使用karma时能够借助其命令行工具完成一些事情,
比如搭建测试框架并生成配置文件 (karma init之类的)
同时karma自己又有许多配置,文件放在 karma.conf.js
配置项包括且不限于
- 使用什么框架
- 是否使用 Require.js
- 默认使用什么浏览器
- 代码位置
- 文件的黑/白名单
- 是否监视文件的变化等等
protractor
在e2e测试时,一个刚需就是要模拟用户在浏览器上的操作.
e2e测试中的一个工具 protractor
也是Angular开发团队给出的工具.
该工具和单元测试一样,使用Jasmine测试框架来定义测试用例.
不同的是, protractor
提供了许多与页面相关的API
1 | browser.get(url) // 访问具体地址 |
更多的API可以参考 官方API文档