Skip to content

单元测试

一. 单元测试的作用与好处

对于一个完整的前端工程来讲, 有单元测试环节是非常有必要的, 特别当你开发的是一个开源库或者插件, 拥有完整的单元测试会让使用者心里有底, 使用起来会更放心, 在同等功能的库面前, 拥有单元测试的库更受欢迎

谁也不能保证自己写的代码里面没有bug, 合适且完整的单元测试能保证将bug出现的概率降到最低, 那么除了这些, 但愿测试还有哪些好处, 我认为有以下几点:

    1. 自动化: 写代码时候肯定避免不了各种调试, 我们平时用的最多的调试无非就是console.log, debugger, 二者在开发中使用, 代码上线时候, 需要移除这些logdebug, 因此不长久, 效率也不高, 编写单元测试可以做到一次编写, 多次执行,提高了效率
    1. 重构保障: 当项目中有一个逻辑特别复杂的屎山代码需要重构, 你能保障重构之后结果是正确的? 逻辑都考虑完全了? 这时候若是这部分代码原先就拥有单元测试, 那重构起来就不害怕会少考虑某些逻辑, 降低心智负担
    1. 保证正确: 通过将某个功能的预期用测试用例的形式编写出来, 功能编写完只需要跑一下测试用例就知道功能是否正常
    1. 减少缺陷率: 一个正常团队, 对缺陷率肯定是有一定的指标的, 若你写一个功能, 都没有自测就甩给测试同学, 那么测试同学肯定会还给你一堆bug, 你也会被测试同学被列入重点关注对象, 因为你到代码很不靠谱, 简单的1 + 1 都会等于 3

前端愿意做单元测试的团队不多, 有的是因为没这方面的意识, 有的压根就不知道单元测试该如何做, 还有的是因为敏捷开发,更多的时间用来完成需求, 快速上线, 根本没时间来做单元测试, 系统稳定性只有靠测试人员的运气和负责任程度来保障

二. 常见测试框和断言库

js测试框架有很多, 目前用的最多的前端单元测试框架主要有mochajest,但推荐你使用 Jest, 因为 JestMocha 相比,无论从github starts & issues 量,npm下载量相比,都有明显优势。

下面是两个框架的对比图, 图片来源传送门alt text

框架断言异步代码覆盖率
Mocha不支持(需要其他库支持)友好不支持(需要其他库支持)
Jest默认支持友好支持
  • Mocha 生态好,但是需要较多的配置来实现高扩展性
  • Jest 开箱即用, 自带断言以及测试覆盖率

1. Mocha + Chai

Mocha需要引入其他断言库去断言, 例如 chai(chai是第三方模块) assert(assert是nodejs自身的断言模块)

Chai 是一个断言库,类似于 Node 的内置assert。它为您提供了大量可以针对您的代码运行的断言,从而使测试变得更加容易。

  1. mocha + assert示例
js
const assert = require("assert");
describe("1. 这是一个测试用例的组", function () {
  describe("组内的第一小组", function () {
    it("测试1+1", function () {
      assert.equal(1 + 1, 2);
    });

    it("测试1+2", function () {
      assert.equal(1 + 2, 2); 
    });
  });
});

scripts配置命令test运行mocha index.js输出如下结果:

bash
> mocha ./index.js

  1. 这是一个测试用例的组
    组内的第一小组
 测试1+1
      1) 测试1+2

  1 passing (5ms)
  1 failing

  1) 1. 这是一个测试用例的组
       组内的第一小组
         测试1+2:

      AssertionError [ERR_ASSERTION]: 3 == 2
      + expected - actual

      -3
      +2
  1. mocha + chai示例
js
const { expect } = require("chai");
describe("1. 这是一个测试用例的组", function () {
  describe("组内的第一小组", function () {
    it("测试1+1", function () {
      expect(1 + 1).to.eq(2);
    });

    it("测试1+2", function () {
      expect(1 + 2).to.equal(2);
    });
  });
});

2. jest

jest

js
const { expect } = require("@jest/globals");

describe("1. 这是一个测试用例的组", function () {
  describe("组内的第一小组", function () {
    it("测试1+1", function () {
      expect(1 + 1).toBe(2);
    });

    it("测试1+2", function () {
      expect(1 + 2).toEqual(2);
    });
  });
});

scripts配置命令test运行jest, jest默认会找/test/**.spec.js或者/test/**.test.js文件

输出如下结果:

bash
> jest

 FAIL  test/index.spec.js
  1. 这是一个测试用例的组
    组内的第一小组
 测试1+1 (3 ms)
      × 测试1+2 (2 ms)

 1. 这是一个测试用例的组 组内的第一小组 测试1+2

    expect(received).toEqual(expected) // deep equality        

    Expected: 2
    Received: 3

       9 |
      10 |     it("测试1+2", function () {
    > 11 |       expect(1 + 2).toEqual(2);
         |                     ^
      12 |     });
      13 |   });
      14 | });

      at Object.toEqual (test/index.spec.js:11:21)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 passed, 2 total
Snapshots:   0 total
Time:        0.378 s
Ran all test suites.

Jest 默认支持断言,同时默认支持覆盖率测试

三. 测试框架和断言库各自的作用

it/test

从上面测试代码可以看出 无论哪个测试框架, 测试用例的书写都是差不多的, 每一个测试用例用it()(有的是test())包起来

it或者test是测试框架提供的函数, 运行测试框架的时候会自动执行it函数, it函数第一个参数是标识这个测试用例的干嘛的, 第二个参数是一个回调函数, 也是测试用例的具体逻辑代码

describe

describe可以用于将一个文件中的测试用例分成多个组, 便于在输出测试结果的时候按照组进行输出, 也是由测试框架提供的

expect

断言, 每一个断言库都会提供一个expect函数, 具体使用方法以及API需要参照不同断言库, 例如jest 匹配器的使用

四. UI界面测试的原理

在编写页面的时候, 可能会设计到一些页面元素的测试, 最显而易见的就是UI组件库, 例如Element-UI,Antd肯定少不了对每一个组件进行单元测试,在前端工程化中, 代码都是运行在开发服务器的, 没有浏览器环境, 是如何进行DOM测试的呢?

原来, 在测试框架内部都集成了一些可以模拟浏览器环境的库, 例如jsDom, 可以在里面模拟一些真实的DOM操作, 比如获取元素, 判断某个元素是否真实存在, 但是对于一些复杂的场景,jsDom无法胜任, 对于这种就该无头浏览器headless登场了, 例如: chromium, 这就和真实浏览器一样,可以做更多更复杂的操作

五. 异步测试

异步在开发中无处不在, jest 测试异步代码