前言:为什么要有进行自动化测试?
经过测试的组件是可靠的,可测试的组件架构是合理的,如果一个组件难以编写测试用例,只能证明组件的设计是糟糕的;编写组件用例的同时,可以帮助组件开发者发现问题,调整代码使架构更加合理。
- 测试可以确保得到预期的结果
- 作为现有代码行为的描述
- 促使开发者写可测试的代码,一般可测试的代码可读性也会高一点
- 如果依赖的组件有修改,受影响的组件能在测试中发现错误
为什么React特别适合组件测试?
1.React是组件化的
2.Functional Component:纯函数是最好写测试的 固定的输入有固定的输出
3.单向数据流:组件不会随便修改外面传入的数据
测试类型
- 单元测试:指的是以原件的单元为单位,对软件进行测试。“单元”的定义取决于自己。如果你正在使用函数式编程,一个单元最有可能指的是一个函数。你的单元测试将使用不同的参数调用这个函数,并断言它返回了期待的结果;在面向对象语言里,下至一个方法,上至一个类都可以是一个单元(从一个单一的方法到一整个的类都可以是一个单元)。 即 单元可以是一个函数,也可以是一个模块或一个组件,基本特征就是只要输入不变,必定返回同样的输出。一个软件越容易些单元测试,就表明它的模块化结构越好,给模块之间的耦合越弱。React的组件化和函数式编程,天生适合进行单元测试。
- 功能测试:相当于是黑盒测试,测试者不了解程序的内部情况,不需要具备编程语言的专门知识,只知道程序的输入、输出和功能,从用户的角度针对软件界面、功能和外部结构进行测试,不考虑内部的逻辑
- 集成测试:在单元测试的基础上,将所有模块按照设计要求组装成子系统或者系统,进行测试
- 冒烟测试:在正式全面的测试之前,对主要功能进行的与测试,确认主要功能是否满足需要,软件是否能正常运行
详解
我们的单元测试,既可以针对一个函数写case,也可以按照函数的调用关系串起来写case。 金字塔模型
在金字塔模型之前,流行的是冰淇淋模型。包含了大量的手工测试、端到端的自动化测试及少量的单元测试。造成的后果是,随着产品壮大,手工回归测试时间越来越长,质量很难把控;自动化case频频失败,每一个失败对应着一个长长的函数调用,到底哪里出了问题?单元测试少的可怜,基本没作用。
Mike Cohn 在他的着作《Succeeding with Agile》一书中提出了**“测试金字塔”**这个概念。这个比喻非常形象,它让你一眼就知道测试是需要分层的。它还告诉你每一层需要写多少测试。 测试金字塔本身是一条很好的经验法则,我们最好记住Cohn在金字塔模型中提到的两件事:
- 编写不同粒度的测试
- 层次越高,你写的测试应该越少
同时,我们对金字塔的理解绝不能止步于此,要进一步理解: 我把金字塔模型理解为——冰激凌融化了。就是指,最顶部的“手工测试”理论上全部要自动化,向下融化,优先全部考虑融化成单元测试,单元测试覆盖不了的 放在中间层(分层测试),再覆盖不了的才会放到UI层。因此,UI层的case,能没有就不要有,跑的慢还不稳定。按照乔帮主的说法,我不分单元测试还是分层测试,统一都叫自动化测试,那就应该把所有的自动化case看做一个整体,case不要冗余,单元测试能覆盖,就要把这个case从分层或ui中去掉。 越是底层的测试,牵扯到相关内容越少,而高层测试则涉及面更广。比如单元测试,它的关注点只有一个单元,而没有其它任何东西。所以,只要一个单元写好了,测试就是可以通过的;而集成测试则要把好几个单元组装到一起才能测试,测试通过的前提条件是,所有这些单元都写好了,这个周期就明显比单元测试要长;系统测试则要把整个系统的各个模块都连在一起,各种数据都准备好,才可能通过。 另外,因为涉及到的模块过多,任何一个模块做了调整,都有可能破坏高层测试,所以,高层测试通常是相对比较脆弱的,在实际的工作中,有些高层测试会牵扯到外部系统,这样一来,复杂度又在不断地提升。
为什么做单测
- 单元测试对我们的产品质量是非常重要的。
- 单元测试是所有测试中最底层的一类测试,是第一个环节,也是最重要的一个环节,是唯一一次有保证能够代码覆盖率达到100%的测试,是整个软件测试过程的基础和前提,单元测试防止了开发的后期因bug过多而失控,单元测试的性价比是最好的。
- 据统计,大约有80%的错误是在软件设计阶段引入的,并且修正一个软件错误所需的费用将随着软件生命期的进展而上升。错误发现的越晚,修复它的费用就越高,而且呈指数增长的趋势。作为编码人员,也是单元测试的主要执行者,是唯一能够做到生产出无缺陷程序这一点的人,其他任何人都无法做到这一点
- 代码规范、优化,可测试性的代码
- 放心重构
- 自动化执行three-thousand times
技术选型之Jest——通用测试框架
一、特点
Jest是Facebook开源的一个前端测试框架,主要用于React和React Native的单元测试,已被集成在create-react-app中。Jest特点:
- 易用性:基于Jasmine,提供断言库,支持多种测试风格
- 适应性:Jest是模块化、可扩展和可配置的
- 沙箱和快照:Jest内置了JSDOM,能够模拟浏览器环境,并且并行执行
- 快照测试:Jest能够对React组件树进行序列化,生成对应的字符串快照,通过比较字符串提供高性能的UI检测
- Mock系统:Jest实现了一个强大的Mock系统,支持自动和手动mock
- 支持异步代码测试:支持Promise和async/await
- 自动生成静态分析结果:内置Istanbul,测试代码覆盖率,并生成对应的报告
二、断言库
1、判断一个值是否对应相应的结果
3、其他断言库:chai等等
三、使用实例
四、Jest Api
globals API
- describe(name, fn):描述块,讲一组功能相关的测试用例组合在一起
- it(name, fn, timeout):别名test,用来放测试用例
- afterAll(fn, timeout):所有测试用例跑完以后执行的方法
- beforeAll(fn, timeout):所有测试用例执行之前执行的方法
- afterEach(fn):在每个测试用例执行完后执行的方法
- beforeEach(fn):在每个测试用例执行之前需要执行的方法
全局和describe都可以有上面四个周期函数,describe的after函数优先级要高于全局的after函数,describe的before函数优先级要低于全局的before函数
beforeAll(() => {
console.log('global before all');
});
afterAll(() => {
console.log('global after all');
});
beforeEach(() =>{
console.log('global before each');
});
afterEach(() => {
console.log('global after each');
});
describe('test1', () => {
beforeAll(() => {
console.log('test1 before all');
});
afterAll(() => {
console.log('test1 after all');
});
beforeEach(() => {
console.log('test1 before each');
});
afterEach(() => {
console.log('test1 after each');
});
it('test sum', () => {
expect(sum(2, 3)).toEqual(5);
});
it('test mutil', () => {
expect(sum(2, 3)).toEqual(7);
});
});
复制代码
复制代码
常见断言
-
expect(value):要测试一个值进行断言的时候,要使用expect对值进行包裹
-
toBe(value):使用Object.is来进行比较,如果进行浮点数的比较,要使用toBeCloseTo
-
not:用来取反
-
toEqual(value):用于对象的深比较
-
toMatch(regexpOrString):用来检查字符串是否匹配,可以是正则表达式或者字符串
-
toContain(item):用来判断item是否在一个数组中,也可以用于字符串的判断
-
toBeNull(value):只匹配null
-
toBeUndefined(value):只匹配undefined
-
toBeDefined(value):与toBeUndefined相反
-
toBeTruthy(value):匹配任何使if语句为真的值
-
toBeFalsy(value):匹配任何使if语句为假的值
-
toBeGreaterThan(number): 大于
-
toBeGreaterThanOrEqual(number):大于等于
-
toBeLessThan(number):小于
-
toBeLessThanOrEqual(number):小于等于
-
toBeInstanceOf(class):判断是不是class的实例
-
anything(value):匹配除了null和undefined以外的所有值
-
resolves:用来取出promise为fulfilled时包裹的值,支持链式调用
-
rejects:用来取出promise为rejected时包裹的值,支持链式调用
-
toHaveBeenCalled():用来判断mock function是否被调用过
-
toHaveBeenCalledTimes(number):用来判断mock function被调用的次数
-
assertions(number):验证在一个测试用例中有number个断言被调用
-
extend(matchers):自定义一些断言
技术选型之Enzyme——通用测试框架
React测试工具
1.React官方测试工具——ReactTestUtils
2.Airbnb基于官方的封装——Enzyme
Enzyme是Airbnb开源的React测试工具库库,它功能过对官方的测试工具库ReactTestUtils的二次封装,提供了一套简洁强大的 API,并内置Cheerio,
实现了jQuery风格的方式进行DOM 处理,开发体验十分友好。在开源社区有超高人气,同时也获得了React 官方的推荐。
一、Enzyme优点
1.简单易懂
2.类似Jquery链式写法
二、两种测试方法
1.Shallow Rendering
2.DOM Rendering
三、安装
yarn add enzyme --save-dev
yarn add enzyme-adapter-react-16 --save-dev
四、使用实例
五、Enzyme API
三种渲染方法
- shallow:浅渲染,是对官方的Shallow Renderer的封装。将组件渲染成虚拟DOM对象,只会渲染第一层,子组件将不会被渲染出来,使得效率非常高。不需要DOM环境, 并可以使用jQuery的方式访问组件的信息
- render:静态渲染,它将React组件渲染成静态的HTML字符串,然后使用Cheerio这个库解析这段字符串,并返回一个Cheerio的实例对象,可以用来分析组件的html结构
- mount:完全渲染,它将组件渲染加载成一个真实的DOM节点,用来测试DOM API的交互和组件的生命周期。用到了jsdom来模拟浏览器环境
三种方法中,shallow和mount因为返回的是DOM对象,可以用simulate进行交互模拟,而render方法不可以。一般shallow方法就可以满足需求,如果需要对子组件进行判断,需要使用render,如果需要测试组件的生命周期,需要使用mount方法。
常用方法
- simulate(event, mock):模拟事件,用来触发事件,event为事件名称,mock为一个event object
- instance():返回组件的实例
- find(selector):根据选择器查找节点,selector可以是CSS中的选择器,或者是组件的构造函数,组件的display name等
- at(index):返回一个渲染过的对象
- get(index):返回一个react node,要测试它,需要重新渲染
- contains(nodeOrNodes):当前对象是否包含参数重点 node,参数类型为react对象或对象数组
- text():返回当前组件的文本内容
- html(): 返回当前组件的HTML代码形式
- props():返回根组件的所有属性
- prop(key):返回根组件的指定属性
- state():返回根组件的状态
- setState(nextState):设置根组件的状态
- setProps(nextProps):设置根组件的属性