前端单元测试到底如何落地?(上)

发表于:2022-3-16 09:58

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:莫疾    来源:三元同学

  前言
  对于现在的前端工程,一个标准完整的项目,通常情况单元测试是非常必要的。但很多时候我们只是完成了项目而忽略了项目测试。我认为其中一个很大的原因是很多人对单元测试认知不够,因此我写了这篇文章,一方面期望通过这篇文章让你对单元测试有一个初步认识。另一个方面希望通过代码示例,让你掌握写单元测试实践能力。
  前端为什么需要单元测试?
  必要性:JavaScript 缺少类型检查,编译期间无法定位到错误,单元测试可以帮助你测试多种异常情况。
  正确性:测试可以验证代码的正确性,在上线前做到心里有底。
  自动化:通过 console 虽然可以打印出内部信息,但是这是一次性的事情,下次测试还需要从头来过,效率不能得到保证。通过编写测试用例,可以做到一次编写,多次运行。
  保证重构:互联网行业产品迭代速度很快,迭代后必然存在代码重构的过程,那怎么才能保证重构后代码的质量呢?有测试用例做后盾,就可以大胆的进行重构。
  现状
  下面是一份抽样调查片段,抽样依据如下:
  ·向 200 名相关者发出在线问卷调查,其中 70 人回答了问卷中的问题,前端人数占 81.16%。
  · 数据收集日期:2021.09.21—2021.10.08
  · 目标群体:所有开发人员
  · 组织规模:不到 50 人,50 到 100人, 100人以上
  你执行过 JavaScript 单元测试吗?
  调查中的另一个有趣的见解是,在大型组织中单元测试更受欢迎。其中一个原因可能是,由于大型组织需要处理大规模的产品,以及频繁的功能迭代吧。这种持续的迭代方式,迫使他们进行自动化测试的投入。更具体地说,单元测试有助于增强产品的整体质量。
  另外,报告显示超 80% 人认为单元测试可以有效的提高质量,超 60% 人使用过 Jest 去编写前端单元测试,超 40% 的人认为单元测试覆盖率是重要的且覆盖率应该大于 80%。
  常见单元测试工具
  目前用的最多的前端单元测试框架主要有 Mocha (https://mochajs.cn/)、Jest (https://www.jestjs.cn/),但我推荐你使用 Jest,因为 Jest 和 Mocha 相比,无论从 github starts & issues 量,npm下载量相比,都有明显优势。
  github stars 以及 npm 下载量的实时数据,参见:jest vs mocha (https://www.npmtrends.com/jest-vs-mocha) 截图日期为 2021.11.25
  Github stars & issues
  npm 下载量
  Jest 的下载量较大,一部分原因是因为 create-react-app 脚手架默认内置了 Jest, 而大部分 react 项目都是用它生成的。
  从 github starts & issues 以及 npm 下载量角度来看,Jest 的关注度更高,社区也更活跃。
  框架对比
  · Mocha 生态好,但是需要较多的配置来实现高扩展性
  · Jest 开箱即用
  比如对 sum 函数写用例
  ./sum.js
  function sum(a, b) {
    return a + b;
  }
  module.exports = sum;

  Mocha + Chai 方式
  Mocha 需要引入 chai 或则其他断言库去断言, 如果你需要查看覆盖率报告你还需要安装 nyc 或者其他覆盖率工具
  ./test/sum.test.js
  const { expect, assert } = require('chai');
  const sum = require('../sum');
  describe('sum', function() {
    it('adds 1 + 2 to equal 3', () => {
      assert(sum(1, 2) === 3);
    });
  });

  Jest 方式
  Jest 默认支持断言,同时默认支持覆盖率测试
  ./test/sum.test.js
  const sum = require('./sum');
  describe('sum function test', () => {
    it('sum(1, 2) === 3', () => {
      expect(sum(1, 2)).toBe(3);
    });
    // 这里 test 和 it 没有明显区别,it 是指: it should xxx, test 是指 test xxx
    test('sum(1, 2) === 3', () => {
      expect(sum(1, 2)).toBe(3);
    });
  })

  可见无论是受欢迎度和写法上,Jest 都有很大的优势,因此推荐你使用开箱即用的 Jest
  如何开始?
  1.安装依赖
  npm install --save-dev jest
 
  2.简单的例子
  首先,创建一个 sum.js 文件
  ./sum.js
  function sum(a, b) {
    return a + b;
  }
  module.exports = sum;

  创建一个名为 sum.test.js 的文件,这个文件包含了实际测试内容:
  ./test/sum.test.js
  const sum = require('../sum');
  test('adds 1 + 2 to equal 3', () => {
    expect(sum(1, 2)).toBe(3);
  });

  将下面的配置部分添加到你的 package.json 里面
  {
    "scripts": {
      "test": "jest"
    },
  }

  运行 npm run test ,jest 将打印下面这个消息。
  3.不支持部分 ES6 语法
  nodejs 采用的是 CommonJS 的模块化规范,使用 require 引入模块;而 import 是 ES6 的模块化规范关键字。想要使用 import,必须引入 babel 转义支持,通过 babel 进行编译,使其变成 node 的模块化代码
  如以下文件改写成 ES6 写法后,运行 npm run test将会报错
  ./sum.js
  export function sum(a, b) {
    return a + b;
  }

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

  报错
  为了能使用这些新特性,我们就需要使用 babel 把 ES6 转成 ES5 语法。
  解决办法
  安装依赖
  npm install --save-dev @babel/core @babel/preset-env

  根目录加入.babelrc
  {   "presets": ["@babel/preset-env"] }

  再次运行 npm run test ,问题解决。
  原理
  jest 运行时内部先执行( jest-babel ),检测是否安装 babel-core,然后取 .babelrc 中的配置运行测试之前结合 babel 先把测试用例代码转换一遍然后再进行测试。
  4.测试 ts 文件
  jest 需要借助 .babelrc 去解析 TypeScript 文件再进行测试
  安装依赖
  npm install --save-dev @babel/preset-typescript

  **改写 **.babelrc
  {   "presets": ["@babel/preset-env", "@babel/preset-typescript"] }

  为了解决编辑器对 jest 断言方法的类型报错,如 test、expect 的报错,你还需要安装
  npm install --save-dev @types/jest

  ./get.ts
  /**
   * 访问嵌套对象,避免代码中出现类似 user && user.personalInfo ? user.personalInfo.name : null 的代码
   */
  export function get<T>(object: any, path: Array<number | string>, defaultValue?: T) : T {
    const result = path.reduce((obj, key) => obj !== undefined ? obj[key] : undefined, object);
    return result !== undefined ? result : defaultValue;
  }

  ./test/get.test.ts
  import { get } from './get';
  test('测试嵌套对象存在的可枚举属性 line1', () => {
    expect(get({
      id: 101,
      email: 'jack@dev.com',
      personalInfo: {
        name: 'Jack',
        address: {
          line1: 'westwish st',
          line2: 'washmasher',
          city: 'wallas',
          state: 'WX'
        }
      }
    }, ['personalInfo', 'address', 'line1'])).toBe('westwish st');
  });

  运行 npm run test

  本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号