单元测试

单元测试

什么是单元测试

单元测试(英语:Unit Testing)又称为模块测试,是针对程序模块软件设计的最小单位)来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向对象编程,最小单元就是方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法。 ——维基百科

单元测试就是开发过程中模块、功能、函数等最小单位的测试。

单元测试在 CI/CD 中的作用?

CI/CD通常指的是持续集成持续交付持续部署的组合实践。

CI 持续集成(Continuous Integration)

成功的 CI 意味着应用代码的新更改会定期构建、测试并合并到共享存储库中。该解决方案可以解决在一次开发中有太多应用分支,从而导致相互冲突的问题。

CD 持续交付和/或持续部署(Continuous Delivery)/(Continuous Deployment)

持续交付

完成 CI 中构建及单元测试和集成测试的自动化流程后,持续交付可自动将已验证的代码发布到存储库。

持续部署

作为持续交付——自动将生产就绪型构建版本发布到代码存储库——的延伸,持续部署可以自动将应用发布到生产环境。

CI-CD

一般情况下,在进行代码的持续集成时,一旦开发人员对应用所做的更改被合并,系统就会通过自动构建应用并运行不同级别的自动化测试(通常是单元测试和集成测试)来验证这些更改,确保这些更改没有对应用造成破坏。

为什么需要单元测试

  1. 必要性:JavaScript 缺少类型检查,编译期间无法定位到错误,单元测试可以帮助你测试多种异常情况。
  2. 正确性:测试可以验证代码的正确性,在上线前做到心里有底。
  3. 自动化:通过编写测试用例,可以做到一次编写,多次运行。
  4. 保证重构:保证代码重构的安全性,在重构代码之后,测试用例能保证重构后的代码功能正常。
  5. 测试驱动开发,指导设计:代码被测试的前提是代码本身的可测试性,那么要保证代码的可测试性,就需要在开发中注意 API 的设计,TDD 将测试前移就是起到这么一个作用,同时测试用例也相当于一个开发文档。

测试框架

框架 断言 异步 代码覆盖率
Mocha 不支持(需要其他库支持)chai 友好 不支持(需要其他库支持)
Jest 默认支持 友好 支持
  • Jest 开箱即用。
  • Mocha 生态较好,但是需要配合其他库一起使用。

什么是断言?

”断言“,个人理解即为”用彼代码断定测试此代码的正确性,检验并暴露此代码的错误。就是判断源码的实际执行结果与预期结果是否一致,如果不一致就抛出一个错误。

怎么做单元测试

  1. 安装依赖

    npm i jest @types/jest babel-jest @babel/core @babel/preset-env -D

    babel.config.js

    module.exports = {
      presets: [["@babel/preset-env", { targets: { node: "current" } }]]
    };
  2. 一个简单的例子尝试

    ./sum.js

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

    创建一个 math.test.js 文件

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

    在 package.json 文件里面加上测试命令

    {
      "scripts": {
        "test": "jest"
      }
    }

    image-20220411142925995

  3. 进行测试并统计覆盖率命令

    image-20220426090936416

    覆盖率统计项

    从覆盖率的图片可以看到一共有 4 个统计项:

    • Stmts(statements):语句覆盖率,程序中的每个语句是否都已执行。
    • Branch:分支覆盖率,是否执行了每个分支。
    • Funcs:函数覆盖率,是否执行了每个函数。
    • Lines:行覆盖率,是否执行了每一行代码。

前端自动化测试

TDD (Test-driven development)

TDD—测试驱动开发,侧重点偏向开发,通过测试用例来规范约束开发者编写出质量更高、bug 更少的代码。先写测试用例,再写功能模块。

测试驱动开发(英语:Test-driven development,缩写为 TDD)是一种软件开发过程中的应用方法,由极限编程中倡导,以其倡导先写测试程序,然后编码实现其功能得名。测试驱动开发始于 20 世纪 90 年代。测试驱动开发的目的是取得快速反馈并使用“illustrate the main line”方法来构建程序。 ——维基百科

TDD 基本流程

image-20220411115056651

TDD 的优点

  • 保证代码质量
  • 测试覆盖率高,因为后编写代码,因此测试用例基本都能照顾到;

TDD 的缺点

  • 代码量增多,大多数情况下测试代码是功能代码的两倍甚至更多;
  • 业务耦合度高,测试用例中使用了业务中一些模拟的数据,当业务代码变更的时候,要去重新组织测试用例;

BDD (Behavior-driven development)

BDD — 行为驱动开发,由外到内的开发方式,从外部定义业务成果,再深入到能实现这些成果,每个成果会转化成为相应的包含验收标准。先写主功能模块,再写测试用例。

行为驱动开发(英语:Behavior-driven development,缩写BDD)是一种敏捷软件开发的技术,它鼓励软件项目中的开发者、QA和非技术人员或商业参与者之间的协作。BDD 最初是由 Dan North 在 2003 年命名[1],它包括验收测试和客户测试驱动等的极限编程的实践,作为对测试驱动开发的回应。 ——维基百科

BDD 的开发流程

1、开发人员和非开发人员一起讨论确认需求

2、以一种自动化的方式将需求建立起来,并确认是否一致

3、最后,实现每个文档示例描述的行为,并从自动化测试开始以指导代码的开发

BDD 的优点:

  • 由于仅关注功能,不关注实现细节,有利于测试代码和实际代码解耦
  • 由于大多数为编写集成测试,相比 TDD 有更好的开发效率

BDD 的缺点:

  • 因为以功能性的集成测试为主,因此不是那么关注每个函数功能,测试覆盖率比较低
  • 没有 TDD 那么严格的保证代码质量

TDD VS BDD

功能 TDD(Test-driven development) BDD(Behavior-driven development)
定义 测试驱动开发 行为驱动开发
思想 白盒测试,从代码角度出发,完成高质量代码为目的。 黑盒测试,从用户角度出发,完成需求为目的。
开发流程 编写单元测试
编写代码
通过测试
代码重构
重复步骤
编写代码
编写测试用例
通过测试
代码重构
重复步骤
代码覆盖率
测试类型 单元测试 集成测试
代码质量 一般
测试代码量

因此个人建议:

  • 在开发组价库,工具函数库时使用 TDD
  • 在开发业务系统时使用 BDD

问题与反思

  1. 单元测试具有学习成本,在哪些地方适合编写单元测试?
  2. 单元测试是否可以实现不同浏览器下的差异性测试?
  3. 在公司项目中进行测试代码的编写可行性有多大?

参考文章

jest 官方文档

浅谈 CI/CD 和软件测试

如何做前端单元测试

浅谈前端单元测试

如何做好前端单元测试

CI/CD 是什么?如何理解持续集成、持续交付和持续部署