一般使用,还是之后难以避免的重构和修正bug过

作者: 前端  发布:2019-11-20

在 2017 年学习 React + Redux 的一些建议(下篇)

2017/09/11 · JavaScript · React, Redux

原文出处: 郭永峰   

在这里说一下前端开发的一个特点是更多的会涉及用户界面,当开发规模达到一定程度时,几乎注定了其复杂度会成倍的增长。

站在产品的角度学习前端知识

前言

随着Web业务的日益复杂化和多元化,前端开发也有了前端工程化的概念,前端工程化成为目前前端架构中重要的一环,本质上也是软件工程的一种,因此我们需要从软件工程的角度来研究前端工程,而自动化测试则是软件工程中重要的一环。本文就研究一下前端领域中的自动化测试,以及如何实践。

9159.com 1关于测试的一些学习建议

我们可以组合使用一些测试工具来帮助测试 JS 代码,一般使用 Mocha/Chai 或是 Karma/Jasmine 。而如果当你想测试 angular 的代码时,你会发现还有更多的测试工具。不过对于 React 应用的测试,比较推荐使用 Airbnb 团队出品的 anzyme 来进行组件的测试,以保住组件的稳定可靠,目前使用非常广泛;而另一种方式是使用 Facebook 的 jest 来进行测试。

可能很多同学都觉得应该选择以上的某一个测试库来进行测试工作,不过,你也可以将 anzymejest 结合起来一起使用。特别是在进行一些 snapshot 快照测试的时候,两种都是互补的,它们已经是 React 应用测试中大家公认的标准库了。

sinon 也是个非常优秀的测试辅助工具,可以帮助我们在 spy、stub、mock 等测试阶段提供相应的工具辅助测试。如果对这几个概念不太清晰,可以看看这里。

另外,在这里给你隆重的给你推荐一篇 A. Sharif 写的 Some Thoughts On Testing React/Redux Applications,满满的干货分享哦。

无论是在代码的初始搭建过程中,还是之后难以避免的重构和修正bug过程中,常常会陷入逻辑难以梳理、无法掌握全局关联的境地。

为什么做测试?我的需求是什么?可以用其他方式解决吗?

什么是单元测试

单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。对于单元测试中单元的含义,一般来说,要根据实际情况去判定其具体含义,如C语言中单元指一个函数,Java里单元指一个类,图形化的软件中可以指一个窗口或一个菜单等。总的来说,单元就是人为规定的最小的被测功能模块。单元测试是在软件开发过程中要进行的最低级别的测试活动,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试。——百度百科

多一些组件的单元测试,少一些集成测试

Enzyme 可以帮助我们实现组件的单元测试和集成测试。这里我们可以通过三种方式来渲染组件:

  • shallow()
  • mount()
  • render()

shallow() 只能用来渲染不包含 children 的组件,mount() 则能够渲染所有的子组件。所以单元测试的时候可以使用 shallow()mount() 则一般用于集成测试。集成测试往往是很容易被割裂的,因为他需要测试由一组或是多个组件树组合的场景,所以集成测试一般维护成本是比较高的。所以我们可以多做一些小巧的单元测试,少做一些重要的集成测试。

第三种测试的方式是使用 render() 方法,具有类似 mount()方法的功能,不过 mount() 能够访问到组件的生命周期方法,比如 componentDidUpdate等。

正如这个 issue 中提出的 API differences between render and mount/shallow,可以总结出:

  • 使用 shallow 开始测试用例
  • 如果 componentDidMount or componentDidUpdate 等方法也需要测试,那么使用 mount 方法吧
  • 如果要测试组件生命周期方法、子组件的行为等,使用 mount 方法吧
  • 如果想更高性能的测试子组件,并且对组件的生命周期等方法不怎么关注,那么使用 render 方法吧

9159.com 2

1、对象 (What)——什么事情

通过TDD测试驱动,检测代码的质量,提高代码可用性,确定工作成果;

用代码检查代码,代替用测试用例文档检查代码,降低人工成本,检测代码的质量,提高代码可用性,让开发人员更懂需求;

为何要测试

以前没有编写和维护测试用例的习惯,在项目的紧张开发周期中也没时间去做这个工作,相信有不少开发人员都不会重视单元测试这项工作。在真正写了一段时间基础组件后,才发现自动化测试有很多好处:

  1. 提升代码质量。虽不能说百分百无bug,但至少说明测试用例覆盖到的场景是没有问题的。
  2. 能快速反馈,能确定UI组件工作情况是否符合自己预期。
  3. 开发者会更加信任自己的代码,也不会惧怕将代码交给别人维护。后人接手一段有测试用例的代码,修改起来也会更加从容。测试用例里非常清楚的阐释了开发者和使用者对于这段代码的期望和要求,也非常有利于代码的传承。

当然由于维护测试用例也是一大笔开销,还是要基于投入产出比来做单元测试。对于像基础组件、基础模型之类的不常变更且复用较多的部分,可以考虑写测试用例来保证质量,但对于迭代较快的业务逻辑及生存时间不长的部分就没必要浪费时间了。

因此github上看到的star较多的牛逼开源前端项目基本上都是有测试代码的,看来业界大牛们都是比较重视单元测试这块的。

保证测试用例简单、最小颗粒度

否则的话你需要为此付出很高的维护成本。

确认每个组件是否都有执行过单元测试,确认每个 props 和 callbacks 都在集成测试的时候传递给了对应的子组件。

为了保证组件测试用例的小颗粒度和简单化,你需要熟悉一下 selectorsEnzyme 提供了丰富的 selector 去深入组件树。

另外,建议使用 sinon 来测试 callbacks 回调函数,不要在组件中测试业务逻辑,这真不是个好注意。而是应该将业务逻辑从组件中解耦并对其进行测试。

最后,Facebook 出品的 Jest 也能在初期帮助我们更加轻量的执行测试,你可以非常简单就设置好 snapshot test,这样当组件的输出改变的话测试用例会自动的报出失败的结果,并且能够获取到错误信息。

而单元测试作为一种“提纲挈领、保驾护航”的基础手段,为开发提供了“围墙和脚手架”,可以有效的改善这些问题。

2、场所 (Where)——什么地点

相关概念

拥抱 TDD(测试驱动开发)

所有的人都可能会对你说:你应该按测试驱动的模式来进行开发。但是,几乎没几个人会这么,项目需求如山的积压,上线的脚本火急火燎,测试驱动?玩呢?!可能大部分小伙伴都是这样的心声。

不过,如果你能够清晰的在 React + Redux 的应用中使用对应的测试方案对每个部分都进行测试,你就能够非常轻松的实现 TDD。尽管你会发现 reducer 的测试和组件的测试是很不一样的,但其实每种类型(reducer、component、…. )的测试模式其实都是一样的。

就拿 reducer 的测试为例吧,一般是期望 reducer(state, action) === newState,其实这种方式和 (input) => output 的模式是一样的。如果你要测试 state 的不可变性的话,建议你可以使用 deep-freeze,可以看下以下示例代码:

JavaScript

import deepFreeze from 'deep-freeze' const initialState = { ... }; const action = { type: ..., payload: ... }; const expectedState = { ... }; deepFreeze(initialState); expect(reducer(initialState, action)).to.equal(expectedState);

1
2
3
4
5
6
7
8
9
import deepFreeze from 'deep-freeze'
 
const initialState = { ... };
const action = { type: ..., payload: ... };
const expectedState = { ... };
 
deepFreeze(initialState);
 
expect(reducer(initialState, action)).to.equal(expectedState);

如果你能够很清晰的知道如何测试应用中的每一个部分,那就最好采用 TDD。

作为一种经典的开发和重构手段,单元测试在软件开发领域被广泛认可和采用;前端领域也逐渐积累起了丰富的测试框架和最佳实践。

3、时间和程序 (When)——什么时候

带写代码之前通过先写测试代码再写业务代码来完成
写完代码之后通过代码测试,或者通过测试文档来完成

TDD

TDD是Test Driven Development 的缩写,也就是测试驱动开发。

通常传统软件工程将测试描述为软件生命周期的一个环节,并且是在编码之后。但敏捷开发大师Kent Beck在2003年出版了 Test Driven Development By Example 一书,从而确立了测试驱动开发这个领域。

TDD需要遵循如下规则:

  • 写一个单元测试去描述程序的一个方面。
  • 运行它应该会失败,因为程序还缺少这个特性。
  • 为这个程序添加一些尽可能简单的代码保证测试通过。
  • 重构这部分代码,直到代码没有重复、代码责任清晰并且结构简单。
  • 持续重复这样做,积累代码。

TDD具有很强的目的性,在直接结果的指导下开发生产代码,然后不断围绕这个目标去改进代码,其优势是高效和去冗余的。所以其特点应该是由需求得出测试,由测试代码得出生产代码。打个比方就像是自行车的两个轮子,虽然都是在向同一个方向转动,但是后轮是施力的,带动车子向前,而前轮是受力的,被向前的车子带动而转。

多组件测试

9159.com 3

4、人员 (Who)——责任人

程序员:通过TDD测试驱动,在写业务代码之前,先把测试代码写完;在写完测试代码之后,在写一段单元测试来检测所写的代码(比较鸡肋);
测试员:在理解需求后,编写测试用例文档,待程序员写完代码后,人工通过测试用例测试成果;

BDD

所谓的BDD行为驱动开发,即Behaviour Driven Development,是一种新的敏捷开发方法。它更趋向于需求,需要共同利益者的参与,强调用户故事(User Story)和行为。2009年,在伦敦发表的“敏捷规格,BDD和极限测试交流”中,Dan North对BDD给出了如下定义:

BDD是第二代的、由外及内的、基于拉(pull)的、多方利益相关者的(stakeholder)、多种可扩展的、高自动化的敏捷方法。它描述了一个交互循环,可以具有带有良好定义的输出(即工作中交付的结果):已测试过的软件。

它对TDD的理念进行了扩展,在TDD中侧重点偏向开发,通过测试用例来规范约束开发者编写出质量更高、bug更少的代码。而BDD更加侧重设计,其要求在设计测试用例的时候对系统进行定义,倡导使用通用的语言将系统的行为描述出来,将系统设计和测试用例结合起来,从而以此为驱动进行开发工作。

大致过程:

  1. 从业务的角度定义具体的,以及可衡量的目标

  2. 找到一种可以达到设定目标的、对业务最重要的那些功能的方法

  3. 然后像故事一样描述出一个个具体可执行的行为。其描述方法基于一些通用词汇,这些词汇具有准确无误的表达能力和一致的含义。例如,expect, should, assert

  4. 寻找合适语言及方法,对行为进行实现

  5. 测试人员检验产品运行结果是否符合预期行为。最大程度的交付出符合用户期望的产品,避免表达不一致带来的问题

关于资源加载的选择

React 虽然是个 library,但是它的生态圈非常的丰富,会有非常多的可扩展框架或类库可以加入使用,但是千万别太快的加入这些扩展方案。并且每次新加入一个模块的时候,要在团队里面确认每个人都是清楚了解的。特别是对于 Redux 本身的一些生态扩展,会有非常多的一些小模块,比如下面这些:

  • 在大家还没开始写 action creatorsreducers 之前,就不要添加 redux-actions
  • 在大家还没写出第一个自己的 form 表单和表单验证的时候,就不要加入 redux-form
  • 在大家还没开始写自己的 selectors 之前,就不要加入 reselect
  • 在大家还么开始写第一个高阶组件 HOC 之前,就不要加入 recompose
  • …..

同时,关注一些大牛的最佳实践,并且建立你自己的最佳实践。不过得确保团队中其他小伙伴也能理解。定义清晰的命名规则和目录结构,并且在项目做一些升级的时候得把这些约定提前讨论清楚。

本文将按如下顺序进行说明:

5、为什么(Why)——原因

  1. 节省成本,提高复用性:
    原本程序员每修改一次代码,就需要测试人员根据测试用例文档,从头到尾的测试,现在程序员每修改一次代码,只需要执行一遍测试用例,即可完成测试。
    原本两个人做的事,现在一个人做完了,原本两个人都要熟悉需求,多一个单位熟悉需求就会出现不可预测的问题,导致延误项目工期;
    现在一个人熟悉完需求,通过测试驱动的方式就可以确保代码质量;

  2. 帮助产品迭代:
    同时可以帮助产品完善需求文档:在写测试用例的时候,发现有需求不明确的,已开发的维度(考虑周全)帮助产品经理补足没有考虑到的方面;

  3. 不需要"业余"的测试人员:
    原本测试人员基本是不知道代码逻辑的,也不清楚哪些地方实现是否是有问题。(公司总会忽悠,说用一个不熟悉代码的人,才能模拟出用户需求,放屁把,普通测试人员根本不清楚用户在场景当下的需求,除非是高级测试人员,那就牛逼哥了,真正能模拟用户需求的只有用户)
    通过程序员严谨的思维,才能帮助产品设计考虑到各种情况下的不足。

  4. 增值:
    提高程序员的代码质量,通过预先思考,帮助程序员分析哪些功能是可以复用的,哪些逻辑是可以简化的。

覆盖率

如何衡量测试脚本的质量呢?其中一个参考指标就是代码覆盖率(coverage)。

什么是代码覆盖率?简而言之就是测试中运行到的代码占所有代码的比率。其中又可以分为行数覆盖率,分支覆盖率等。具体的含义不再细说,有兴趣的可以自行查阅资料。

虽然并不是说代码覆盖率越高,测试的脚本写得越好,但是代码覆盖率对撰写测试脚本还是有一定的指导意义的。

保持持续的技术学习热情

  • 关注技术社区的新动向,比如在应用中使用 ramda.js.,看看如何在React中优雅的写代码
  • 学习如何使用 React Native 构建你的移动应用
  • 学习使用 Electron 构建你的桌面应用
  • 也许你可以关注如何使用 Mobx 来进行应用状态管理
  • React 仅仅只是 UI 层的一个 library ,你可以使用PREACT 和 inferno 等类似 React 的库来替代,他们的体积能加轻量,渲染更加高效,也许是个不错的选择。
  • Airbnb 的 React/JSX 规范 我也建议你抽时间看看,对于团队一致化开发非常有帮助。同时,也可以使用 ESLint 来进行代码规则检查。
  • I. 单元测试简介
  • II. React 单元测试中用到的工具
  • III. 用测试驱动 React 组件重构
  • IV. React 单元测试常见案例

6、方式 (How)——如何

前端单测工具栈

结束语

全文完结,感谢你的阅读,希望整个系列的文章对你今后的学习有所帮助。

如果你想系统学习 React + Redux 技术栈的所有内容,请点我前往

2 赞 1 收藏 评论

9159.com 4

单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。

工具介绍
  • karma:
    • Karma是一个基于Node.js的JavaScript测试执行过程管理工具
    • 该工具可用于测试所有主流Web浏览器,也可集成到CI(Continuous integration)工具,也可和其他代码编辑器一起使用
    • 这个测试工具的一个强大特性就是,它可以监控(Watch)文件的变化,然后自行执行,通过console.log显示测试结果
  • Jasmine:
    • Jasmine (茉莉)是一款 JavaScript BDD(行为驱动开发)测试框架,它不依赖于其他任何 JavaScript 组件。它有干净清晰的语法,让您可以很简单的写出测试代码
  • Mocha:测试框架。提供describe,it,beforeEach等函数管理你的 testcase
    • 简单、易上手
    • 支持TDD、BDD等多种接口
    • 支持同步和异步的测试
    • 9159.com,支持多种方式导出结果
    • 支持直接在浏览器上跑JavaScript代码
  • Chai:
    • BDD(行为驱动开发)和TDD(测试驱动开发)双测试风格的断言库
  • enzyme:
    • React测试工具,可以类似 jquery 风格的 api 操作react 节点
  • Sinon:
    • 提供 fake 数据, 替换函数调用等功能
  • Jest
    • Facebook 官方支持
    • 适应性:Jest是模块化、可扩展和可配置的。
    • 沙箱和快速:Jest虚拟化了JavaScript的环境,能模拟浏览器,并且并行执行
    • 快照测试:Jest能够对React 树进行快照或别的序列化数值快速编写测试,提供快速更新的用户体验。
    • 支持异步代码测试:支持promises和async/await

测试框架

主要提供了清晰简明的语法来描述测试用例,以及对测试用例分组,测试框架会抓取到代码抛出的AssertionError,并增加一大堆附加信息,比如那个用例挂了,为什么挂等等。目前比较流行的测试框架有:

  • Jasmine: 自带断言(assert),mock功能
  • Mocha: 框架不带断言和mock功能,需要结合其他工具,由tj大神开发
  • Jest: 由Facebook出品的测试框架,在Jasmine测试框架上演变开发而来

简单来说,单元就是人为规定的最小的被测功能模块。单元测试是在软件开发过程中要进行的最低级别的测试活动,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试。

组合:

karma + Jasmine VS Mocha + Chai + Sinon VS Jest + Enzyme

karma + Jasmine:虽然很流行,但是在react体系中不推荐;更适用于AngularJS;

Mocha + Chai + SinonJest + Enzyme 各有千秋,都可以先参数看看,喜欢用哪个选哪个;

  • redux中文文档推荐使用Jest + Enzyme Redux中文文档 -编写测试
  • react官方也推荐使用 Jest

前期入门多学习学习,多尝试,没坏处;可以对比两种测试用例的差异;选择自己喜欢的写法;

断言库

断言库提供了很多语义化的方法来对值做各种各样的判断。

  • chai: 目前比较流行的断言库,支持 TDD(assert),BDD(expect、should)两种风格
  • should.js:也是tj大神所写

测试框架

测试框架的作用是提供一些方便的语法来描述测试用例,以及对用例进行分组。

参考资料

  • 阮大大 - React 测试入门教程
  • 测试驱动编写 React 简易计算器
  • React+Redux单元测试一小时入门
  • React 测试驱动教程
  • 使用 AVA 和 Enzyme 测试 React 组件(三)
  • React中文文档 - 测试工具集
  • 测试框架 Mocha 实例教程
  • 使用Mocha + Chai + Sinon 测试React + Redux的web应用

mock库

  • sinon.js:使用Sinon,我们可以把任何JavaScript函数替换成一个测试替身。通过配置,测试替身可以完成各种各样的任务来让测试复杂代码变得简单。支持 spies, stub, fake XMLHttpRequest, Fake server, Fake time,很强大

断言(assertions)

断言是单元测试框架中核心的部分,断言失败会导致测试不通过,或报告错误信息。

对于常见的断言,举一些例子如下:

  • 同等性断言 Equality Asserts

    • expect.toEqual
    • expect.not.toEqual
  • 比较性断言 Comparison Asserts

    • expect.toBeGreaterThan
    • expect.toBeLessThanOrEqual
  • 类型性断言 Type Asserts

    • expect.toBeInstanceOf
  • 条件性测试 Condition Test

    • expect.toBeTruthy()
    • expect.toBeFalsy()
    • expect.toBeDefined()

测试集成管理工具

  • karma:Google Angular 团队写的,功能很强大,有很多插件。可以连接真实的浏览器跑测试。能够用一些测试覆盖率统计的工具统计一下覆盖率;或是能够加入持续集成,提交代码后自动跑测试用例。

断言库

断言库主要提供上述断言的语义化方法,用于对参与测试的值做各种各样的判断。这些语义化方法会返回测试的结果,要么成功、要么失败。常见的断言库有 Should.js, Chai.js 等。

测试脚本的写法

通常,测试脚本与所要测试的源码脚本同名,但是后缀名为.test.js(表示测试)或者.spec.js(表示规格)。

// add.test.js
var add = require('./add.js');
var expect = require('chai').expect;

describe('加法函数的测试', function() {
  it('1 加 1 应该等于 2', function() {
    expect(add(1, 1)).to.be.equal(2);
  });
});

上面这段代码,就是测试脚本,它可以独立执行。测试脚本里面应该包括一个或多个describe块,每个describe块应该包括一个或多个it块。

describe块称为"测试套件"(test suite),表示一组相关的测试。它是一个函数,第一个参数是测试套件的名称("加法函数的测试"),第二个参数是一个实际执行的函数。

describe干的事情就是给测试用例分组。为了尽可能多的覆盖各种情况,测试用例往往会有很多。这时候通过分组就可以比较方便的管理(这里提一句,describe是可以嵌套的,也就是说外层分组了之后,内部还可以分子组)。另外还有一个非常重要的特性,就是每个分组都可以进行预处理(before、beforeEach)和后处理(after, afterEach)。

it块称为"测试用例"(test case),表示一个单独的测试,是测试的最小单位。它也是一个函数,第一个参数是测试用例的名称("1 加 1 应该等于 2"),第二个参数是一个实际执行的函数。

大型项目有很多测试用例。有时,我们希望只运行其中的几个,这时可以用only方法。describe块和it块都允许调用only方法,表示只运行某个测试套件或测试用例。此外,还有skip方法,表示跳过指定的测试套件或测试用例。

describe.only('something', function() {
  // 只会跑包在里面的测试
})

it.only('do do', () => {
  // 只会跑这一个测试
})

测试用例 test case

为某个特殊目标而编制的一组测试输入、执行条件以及预期结果,以便测试某个程序路径或核实是否满足某个特定需求。

一般的形式为:

it('should ...', function() { ... expect.toEqual;

react 单测示例一

react 单元测试框架 demo1

该框架采用 karma + mocha + chai + sinon 的组合, 是一种采用工具较多,同时自由度较高的解决方案。虽然工具库使用的较多,但有助于理解各个工具库的作用和使用,也有助于加深对前端单元测试的理解。

其中React的测试库使用 enzyme,React测试必须使用官方的测试工具库,但是它用起来不够方便,所以有人做了封装,推出了一些第三方库,其中Airbnb公司的Enzyme最容易上手。

关于该库的 api 使用可参考:

官方文档

阮老师React测试入门

测试套件 test suite

通常把一组相关的测试称为一个测试套件

一般的形式为:

describe('test ...', function() { it('should ...', function() { ... }); it('should ...', function() { ... }); ...});

react 单测示例二

react 单元测试框架 demo2

该框架只采用了Jest,是比较简洁的方案,同样也使用了 enzyme。

Jest 是Facebook开发的一个测试框架,它集成了测试执行器、断言库、spy、mock、snapshot和测试覆盖率报告等功能。React项目本身也是使用Jest进行单测的,因此它们俩的契合度相当高。
之前仅在其内部使用,后开源,并且是在Jasmine测试框架上演变开发而来,使用了熟知的expect(value).toBe(other)这种断言格式。

PS: 目前 enzyme 使用时需要加入设置如下:

import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

Enzyme.configure({ adapter: new Adapter() });

上面两个框架方案中都有加入该配置的方法,详见示例。

spy

正如 spy 字面的意思一样,我们用这种“间谍”来“监视”函数的调用情况

通过对监视的函数进行包装,可以通过它清楚的知道该函数被调用过几次、传入什么参数、返回什么结果,甚至是抛出的异常情况。

var spy = sinon.spy(MyComp.prototype, 'componentDidMount');...expect(spy.callCount).toEqual;

参考

  • 聊一聊前端自动化测试
  • 前端自动化单元测试初探
  • Javascript的Unit Test
  • 单元测试all in one
  • Sinon指南: 使用Mocks, Spies 和 Stubs编写JavaScript测试
  • 测试框架 Mocha 实例教程

stub

有时候会使用stub来嵌入或者直接替换掉一些代码,来达到隔离的目的

一个stub可以使用最少的依赖方法来模拟该单元测试。比如一个方法可能依赖另一个方法的执行,而后者对我们来说是透明的。好的做法是使用stub 对它进行隔离替换。这样就实现了更准确的单元测试。

var myObj = { prop: function() { return 'foo'; }};sinon.stub(myObj, 'prop').callsFake(function() { return 'bar';});myObj.prop(); // 'bar'

mock

mock一般指在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法

广义的讲,以上的 spy 和 stub 等,以及一些对模块的模拟,对 ajax 返回值的模拟、对 timer 的模拟,都叫做 mock 。

测试覆盖率(code coverage)

用于统计测试用例对代码的测试情况,生成相应的报表,比如 istanbul 是常见的测试覆盖率统计工具

Jest

9159.com 5

不同于"传统的"的 jasmine / Mocha / Chai 等前端测试框架 -- Jest的使用更简单,并且提供了更高的集成度、更丰富的功能。

Jest 是 Facebook 出品的一个测试框架,相对其他测试框架,其一大特点就是就是内置了常用的测试工具,比如自带断言、测试覆盖率工具,实现了开箱即用。

此外, Jest 的测试用例是并行执行的,而且只执行发生改变的文件所对应的测试,提升了测试速度。

编写单元测试的语法通常非常简单;对于jest来说,由于其内部使用了 Jasmine 2 来进行测试,故其用例语法与 Jasmine 相同。

实际上,只要先记这住四个单词,就足以应付大多数测试情况了:

  • describe: 定义一个测试套件
  • it:定义一个测试用例
  • expect:断言的判断条件
  • toEqual:断言的比较结果
describe('test ...', function() { it('should ...', function() { expect.toEqual; expect(sth.length).toEqual; expect(sth > oth).toEqual;});

Jest 号称自己是一个 “Zero configuration testing platform”,只需在 npm scripts里面配置了test: jest,即可运行npm test,自动识别并测试符合其规则的(一般是 __test__ 目录下的)用例文件。

实际使用中,适当的自定义配置一下,会得到更适合我们的测试场景:

//jest.config.jsmodule.exports = { modulePaths: [ "<rootDir>/src/" ], moduleNameMapper: { ".$": '<rootDir>/__test__/NullModule.js' }, collectCoverage: true, coverageDirectory: "<rootDir>/src/", coveragePathIgnorePatterns: [ "<rootDir>/__test__/" ], coverageReporters: ["text"],};

在这个简单的配置文件中,我们指定了测试的“根目录”,配置了覆盖率(内置的istanbul)的一些格式,并将原本在webpack中对样式文件的引用指向了一个空模块,从而跳过了这一对测试无伤大雅的环节

//NullModule.jsmodule.exports = {};

另外值得一提的是,由于jest.config.js是一个会在npm脚本中被调用的普通 JS 文件,而非XXX.json.XXXrc的形式,所以 nodejs 的各自操作都可以进行,比如引入 fs 进行预处理读写等,灵活性非常高,可以很好的兼容各种项目

由于是面向src目录下测试其React代码,并且还使用了ES6语法,所以项目下需要存在一个.babelrc文件:

{ "presets": ["env", "react"]}

以上是基本的配置,而实际由于webpack可以编译es6的模块,一般将babel中设为{ "modules": false },此时的配置为:

//package.json"scripts": { "test": "cross-env NODE_ENV=test jest",},

//.babelrc{ "presets": [ ["es2015", {"modules": false}], "stage-1", "react" ], "plugins": [ "transform-decorators-legacy", 如果对软件测试、接口测试、自动化测试、性能测试、LR脚本开发、面试经验交流。 "react-hot-loader/babel" 感兴趣可以175317069,群内会有不定期的发放免费的资料链接,这些资料都是从各 ], 个技术网站搜集、整理出来的,如果你有好的学习资料可以私聊发我,我会注明出处 "env": { 之后分享给大家。 "test": { "presets": [ "es2015", "stage-1", "react" ], "plugins": [ "transform-decorators-legacy", "react-hot-loader/babel" ] } }}

Enzyme

Enzyme 来自于活跃在 JavaScript 开源社区的 Airbnb 公司,是对官方测试工具库(react-addons-test-utils)的封装。

这个单词的伦敦读音为 ['enzaɪm],酵素或酶的意思,Airbnb 并没有给它设计一个图标,估计就是想取用它来分解 React 组件的意思吧。

它模拟了 jQuery 的 API,非常直观并且易于使用和学习,提供了一些与众不同的接口和几个方法来减少测试的样板代码,方便判断、操纵和遍历 React Components 的输出,并且减少了测试代码和实现代码之间的耦合。

一般使用 Enzyme 中的 mountshallow 方法,将目标组件转化为一个 ReactWrapper对象,并在测试中调用其各种方法:

import Enzyme,{ mount } from 'enzyme';...describe('test ...', function() { it('should ...', function() { wrapper = mount( <MyComp isDisabled={true} /> ); expect( wrapper.find.exists.toBeTruthy;

sinon

9159.com 6

图中这位“我牵着马”的并不是卷帘大将沙悟净...其实图中的故事正是人所皆知的“特洛伊木马”;大概意思就是希腊人围困了特洛伊人十多年,久攻不下,心生一计,把营盘都撤了,只留下一个巨大的木马,以及这位被扒光还被打得够呛的人,也就是此处要谈的主角sinon,由他欺骗特洛伊人 --- 后面的剧情大家就都熟悉了。

所以这个命名的测试工具呢,也正是各种伪装渗透方法的合集,为单元测试提供了独立而丰富的 spy, stub 和 mock 方法,兼容各种测试框架。

虽然 Jest 本身也有一些实现 spy 等的手段,但 sinon 使用起来更加方便。

这里不展开讨论经典的 “测试驱动开发”(TDD - test driven development) 理论

简单的说,把测试正向加诸开发,先写用例再逐步实现,就是TDD,这是很好理解的。

而当我们反过头来,对既有代码补充测试用例,使其测试覆盖率不断提高,并在此过程中改善原有设计,修复潜在问题,同时又保证原有接口不收影响,这种 TDD 行为虽然没人称之为“测试驱动重构”(test driven refactoring),但“重构”这个概念本身就包含了用测试保驾护航的意思,是必不可少的题中之意。

对于一些组件和共有函数等,完善的测试也是一种最好的使用说明书。

失败-编码-通过 三部曲

由于测试结果中,成功的用例会用绿色表示,而失败的部分会显示为红色,所以单元测试也常常被称为 “Red/Green Testing” 或 “Red/Green Refactoring” , 这也是 TDD 中的一般性步骤:

  1. 添加一个测试
  2. 运行所有测试,看看新加的这个是不是失败了;如果能成功则重复步骤1
  3. 根据失败报错,有针对性的编写或改写代码;这一步的唯一目的就是通过测试,先不必纠结细节
  4. 再次运行测试;如果能成功则跳到步骤5,否则重复步骤3
  5. 重构已经通过测试的代码,使其更可读、更易维护,且不影响通过测试
  6. 重复步骤1

9159.com 79159.com 8

如果对软件测试、接口测试、自动化测试、性能测试、LR脚本开发、面试经验交流。感兴趣可以175317069,群内会有不定期的发放免费的资料链接,这些资料都是从各个技术网站搜集、整理出来的,如果你有好的学习资料可以私聊发我,我会注明出处之后分享给大家。

解读测试覆盖率

9159.com 9

这就是 jest 内置的 istanbul 输出的覆盖率结果。

之所以叫做“伊斯坦布尔”,是因为土耳其地毯世界闻名,而地毯是用来"覆盖"的

本文由9159.com发布于前端,转载请注明出处:一般使用,还是之后难以避免的重构和修正bug过

关键词: