前端基础能力 - Jest前端测试框架 - 图1

前言

测试是整个项目保障最重要的一环,关系到最终软件的产出质量, 测试对后端来说相对比较熟悉,包括 接口测试,单元测试,性能测试,流量压测 等。但对于前端来说相对比较陌生,由于前端偏向于 GUI 软件性质,同时国内快速的业务迭代节奏,前端做自动化测试的投入产出比不高。

Jest测试环境源码地址

源码地址:https://github.com/dkypooh/front-end-develop-demo/tree/master/base/jest

知乎讨论 - TDD(测试驱动开发)是否已死?

  1. 自动化的收益 = 迭代次数 * 全手动执行成本 - 首次自动化成本 - 维护次数 * 维护成本

解释: 计算公式可以看出来首次投入的成本远远小于首次收益,但是随着项目迭代,收益会越来越明显。

作者认为: 对于前端是否适合做自动化测试,不能一棒子肯定或者否定,需要辩证根据不同的应用场景,项目重要程度(资金相关应用)来判断。下面列举了一些场景

  • 数据层SDK: 不涉及UI表现,需要做单元测试,接口覆盖率测试等,它是提供服务给UI层,还需要考虑业务接入版本管理问题。 它的投入产出比远远超出预期。
  • 资损类型项目: 对于资金相关的项目,需要重点保障,接口或者UI的改变都可能导致项目严重故障。它的投入产出比也远远超出预期。
  • 其他非核心保障项目: 量力而行,不要为了 TDDTDD

Jest前端测试框架

Jest 是 Facebook 出品的一个测试框架,相对其他测试框架,最大的特点就是内置了常用的测试工具,比如 自带断言测试覆盖率工具UI测试工具Mock能力 等,同时可以集成很多插件,与主流的软件库配合测试,比如:Typescript, React, Vue等, 真正实现了开箱即用。

基本配置

  1. $ npm install --save-dev jest

创建一个 sum.js 文件

  1. function sum(a, b) {
  2. return a + b;
  3. }
  4. module.exports = sum;

创建一个 sum.jest.js 文件

  1. const sum = require('./sum');
  2. test('adds 1 + 2 to equal 3', () => {
  3. expect(sum(1, 2)).toBe(3);
  4. });

package.json创建 NPM Scripts

  1. {
  2. "scripts": {
  3. "test": "jest"
  4. }
  5. }

运行 npm run test 查看结果

  1. PASS ./sum.test.js
  2. adds 1 + 2 to equal 3 (5ms)

配置文件

初始化生成 jest.config.js 文件,可以选择 nodejsdom 两种环境,暂时选择 node 环境,之后根据项目需求配置 jest.config.js 文件

  1. $ npx jest --init
  2. ## 执行jest测试
  3. $ npx jest -c jest.config.js --colors

Jest全局变量及生命周期

在您的测试文件,Jest 将这些方法和对象放入全局环境。你不必导入即可使用它们。
前端基础能力 - Jest前端测试框架 - 图2

afterAll 测试用例

afterAll 测试用例为例,其他生命周期也是一样。 解释下面用例:创建全局数据库,查询事物,等所有调用结束后关闭数据库。

  1. const globalDatabase = makeGlobalDatabase();
  2. function cleanUpDatabase(db) {
  3. db.cleanUp();
  4. }
  5. afterAll(() => {
  6. cleanUpDatabase(globalDatabase);
  7. });
  8. test('can find things', () => {
  9. return globalDatabase.find('thing', {}, results => {
  10. expect(results.length).toBeGreaterThan(0);
  11. });
  12. });

Typescript 单元接口测试

Typescript 由于强类型检查,代码提示等优秀的特性,越来越成为主流开发语言。Jest 也很好的支持了 Typescript 语法。Typescript 在数据 SDK 接口模块开发中发挥了重要角色,考虑到之后的维护性和扩展性,对于 SDK 提供基础能力模块必须要用 Typescript 开发,同时配合 Jest 做单元测试和覆盖率测试,到达事半功倍的效果。

参考 前端高阶能力 - 通用SDK设计 案例

搭建Typescript测试环境

安装 typescript, @types/jest, jest, ts-jest 依赖。

详细配置参考 Using Jest with TypeScript

  1. {
  2. "devDependencies": {
  3. "typescript": "^3.3.1",
  4. "@types/jest": "^24.0.0",
  5. "jest": "^24.1.0",
  6. "ts-jest": "^23.10.5"
  7. }
  8. }

配置 jest.config.js

使用 ts-jest 插件进行 .ts 的语法转化。

  1. module.exports = {
  2. transform: {
  3. '^.+\\.ts?$': 'ts-jest',
  4. },
  5. testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(js?|ts?)$',
  6. moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
  7. }

SDK 测试用例

  1. import SDK from '../src/index'
  2. describe('SDK Test', () => {
  3. const sdk = new SDK([]);
  4. it('subscribe and publish', (done) => {
  5. sdk.on('publish', (obj) => {
  6. expect(obj).toEqual({cmd: 'publish'});
  7. done();
  8. })
  9. sdk.emit('publish', {cmd: 'publish'});
  10. });
  11. it('add middleware module', (done) => {
  12. sdk.useBatch([(ctx: any) => {
  13. ctx.message.content = 'test';
  14. }, (ctx: any) => {
  15. ctx.conversation.lastMsg = 'test';
  16. }])
  17. sdk.dispatch({type: 'text'}, {id: 'yyy'}).then((ctx) => {
  18. expect(ctx.message).toEqual({ type: 'text', content: 'test' })
  19. expect(ctx.conversation).toEqual({ id: 'yyy', lastMsg: 'test' })
  20. done();
  21. })
  22. })
  23. })

React 测试

对于核心资损业务,不管是 接口层 SDK 还是 UI层 改动都会存在风险。Jest 也提供了 React UI 测试能力。同时配合 enzyme 断言和控制 UI 组件渲染。

enzyme 是 Airbnb开源的 React 测试类库, 提供了一套简洁强大的 API,并通过 jQuery 风格的方式进行DOM 处理。

  1. 源码参考地址: https://github.com/dkypooh/front-end-develop-demo/tree/master/base/jest

测试实例

CheckboxWithLabel-test.js 测试例子, enzyme shallow 方法渲染 DOM 元素,

  1. import React from 'react';
  2. import Enzyme, {shallow} from 'enzyme';
  3. import Adapter from 'enzyme-adapter-react-16';
  4. Enzyme.configure({adapter: new Adapter()}); // 设置适配器
  5. import CheckboxWithLabel from '../src/components/CheckboxWithLabel';
  6. it('CheckboxWithLabel changes the text after click', () => {
  7. // Render a checkbox with label in the document
  8. const checkbox = shallow(<CheckboxWithLabel labelOn="On" labelOff="Off" />);
  9. expect(checkbox.text()).toEqual('Off');
  10. checkbox.find('input').simulate('change');
  11. expect(checkbox.text()).toEqual('On');
  12. });

快照

每当你想要确保你的UI不会有意外的改变,快照测试是非常有用的工具。

一个典型的移动app快照测试案例过程是,先渲染UI组件,然后截图,最后和独立于测试存储的参考图像进行比较。

上文提到的 重点资损项目 可以监测UI改动差异,是否符合预期

  1. 源码参考地址: https://github.com/dkypooh/front-end-develop-demo/blob/master/base/jest/__test__/link.test.js

测试用例

  1. it('renders correctly', () => {
  2. const tree = renderer
  3. .create(<Link page="http://www.facebook.com">Facebook</Link>)
  4. .toJSON();
  5. expect(tree).toMatchSnapshot();
  6. });

修改链接 facebooktaobao, 运行 npm run test。运行结果如下:

  1. - Snapshot
  2. + Received
  3. <a
  4. className="normal"
  5. - href="http://www.facebook.com"
  6. + href="http://www.taobao.com"
  7. onMouseEnter={[Function]}
  8. onMouseLeave={[Function]}
  9. >
  10. Facebook
  11. </a>

Code Review

前端基础能力 - Jest前端测试框架 - 图3
Code Review 是阿里巴巴最为看重保证质量的环节。 尤其在双十一期间上线需求都需要多人多次 Code Review 保证上线质量。

Code Review 的好处显然易见,一方面,方面能够在及早发现代码中潜在的bug,统一团队的代码规范。 另一方面, Review 过程也是相互学习的过程, 同时可以对项目做好 Backup 需求。

Code Review 形式可以灵活多变,前期需要有人审核你的代码,同时优化你代码。 本章提出来 希望大家重视 Code Review 过程, 对保证项目质量非常重要

结语

作者认为测试是保证项目质量的最重要的环节。由于前端的特性,我们需要根据当时的场景和项目情况来合理安排测试,力争投入产出比最高。

通过本章的学习,我们了解 Jest 前端测试框架的能力,以及 Jest 全局变量和生命周期,Snapshot 快照的能力引入有效的减少UI改变带来的风险,最后,Code Review 是代码质量最重要的保障措施。

思考题

Q: 使用上文Jest项目,实现一个UI快照用例? 参考地址:https://jestjs.io/docs/zh-Hans/snapshot-testing

参考文档