使用 Jest 进行单元测试的基础知识

2,047 阅读3分钟

这是我参与新手入门第二篇文章

大家好! 今天我给大家带来一篇关于 JavaScript 测试。有各种不同类型的测试,我们将涵盖单元测试的基础知识,该基础是测试应用程序的各个部分,并检查它们是否适合使用。为此,我们将使用 Jest,一个由 Facebook 开发的测试框架。它已经准备就绪,并配备了我们将需要进行测试的功能。

Let’s go!

测试类型

测试是检查代码的代码。

测试可能会让你对应用程序感到更加自信。它们还将阻止你在破坏另一件事时修复一件事。

你可以从单个函数及其返回值到运行在浏览器中的复杂应用程序中的许多方面测试你的应用程序的许多方面。

接下来我们将简要比较流行的测试类型。

单元测试

单元测试涵盖了代码块,以确保它们能够毫无问题地运行。

  • 一个被测试的单元可以是一个函数、一个模块或一个类。
  • 单元测试应彼此隔离并独立。
  • 对于给定的输入,单元测试将检查结果。

通过及早发现问题并避免回归,它可以帮助我们确保应用程序的各个部分按照预期工作。

集成测试

即使我们的所有单元测试都通过了,它仍然只是意味着各个部分自己工作得很好。尽管如此,应用程序可能会失败。集成测试包括跨模块流程,其中单个模块在一起工作时进行组合和测试。感谢他们,让我们可以提供一种确保代码整体良好工作的方法。

端到端测试

与其他类型的测试不同,端到端(E2E)测试总是在浏览器(或类似浏览器的)环境中运行。它可能是一个实际的浏览器,打开并在其中运行测试。它也可能是无头浏览器环境,即没有用户界面的浏览器。端到端测试的目的是在我们运行的应用程序中模拟一个实际的用户。它们将模拟滚动、点击和输入等行为,并从实际用户的角度检查我们的应用程序是否工作良好。这里推荐 cypress 测试工具

使用 Jest 进行单元测试

Jest 是 Facebook 开发的测试框架。其中一个目标是提供一种“零配置”体验,工具准备用开箱即用。它已经存在了一段时间,它是快速可靠的。

让我们来使用它!

npm install --save-dev jest

请记住也将其添加到 npm scripts 中。

package.json

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

为简单起见,我将使用简单的纯 Node.js 模块使用Jest。

首先,让我们创建一些我们可以测试的简单功能。

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

Jest 使用正则表达式来确定哪些文件是测试文件。默认情况下:

  • tests目录中 .js.jsx 文件
  • .test.spec 后缀文件
  • 自定义指定测试文件,使用 testRegex 属性修改

package.json

"jest": {
  "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.jsx?$"
}

最后,让我们创建测试文件。为了遵循默认的命名配置,我们将其命名为 add.test.js

add.test.js

const add = require('./add');
 
test('adding 1 by 2 equals 3', () => {
  expect(add(1, 2)).toBe(3);
});

使用 npm run test 运行该测试:

PASS  ./add.test.js
  ✓ adding 1 by 2 equals 3 (2ms)

test 函数运行一个测试用例。它接受三个参数: 测试名称、包含期望的函数和超时(以毫秒为单位)。超时默认为5秒,并指定如果测试花费的时间太长,在中止测试之前等待的时间。

expect 函数用于测试一个值。它接受参数是我们想要测试的值:在我们的示例中,它是 add 函数的返回值。我们可以调用一组匹配器函数(就像示例中使用的 toBe 一样)以某种方式测试值。要获得完整列表,请访问 Jest文档

说几个常用的:

  • toBe 比较两个值 内部使用 Object.is
  • toEqual 深度比较两个值 内部使用 Object.is
  • toStrictEqual 深度比较两个值 一般用于检查嵌套引用类型
  • toBeFalsy 返回 false
  • toBeTruthy 返回 true
  • toContain 数组或者字符串是否包含指定的值 类似 includes 方法
  • toMatch 匹配字符串 类似 match 方法 它可以接收字符串和正则表达式
  • toThrow 接收 Error 对象
  • resolves 接收 Promise.resolve 返回值
  • rejects 接收 Promise.reject 返回值
  • not 测试结果取反

自定义匹配器函数:

expect.extend({
  // received 为 expect 的结果
  // 其他参数是匹配器函数的参数
  // 返回 message 提示文本 和 pass 测试通过状态 
  toBeWithinRange(received, floor, ceiling) {
    const pass = received >= floor && received <= ceiling;
    if (pass) {
      return {
        message: () =>
          `expected ${received} not to be within range ${floor} - ${ceiling}`,
        pass: true,
      };
    } else {
      return {
        message: () =>
          `expected ${received} to be within range ${floor} - ${ceiling}`,
        pass: false,
      };
    }
  },
});

分组测试

每份文件通常会有多个测试。借助 Jest,我们可以使用 describe 函数对它们进行分组。它创建一个可以组合多个测试的块。更好地可视化它,让我们多运行一些测试。

describe('add utils', () => {
    it('adding 1 by 2 equals 3', () => {
      expect(add(1, 2)).toBe(3);
    })
    it('adding 1 by '2' equals "12"', () => {
      expect(add(1, '2')).toBe('12');
    })
})

你可能会注意到 it 函数的用法而不是 test 函数。ittest 的一个别名,it 是通用测试用例名字。推荐使用,这样你可以更方便移植其他测试工具中。

PASS  ./add.test.js
  ✓ add utils
  · adding 1 by 2 equals 3 (2ms)
  · adding 1 by '2' equals "12" (2ms)

常用技巧

  1. 自动运行测试
npm run test --watch
  1. 测试覆盖率
npm run test --coverage=true

运行完测试,自动生成文件 /coverage, index.html 显示测试的覆盖率,测试对应的 html 文件,显示测试覆盖率结果。

  1. 测试某个文件夹
npm run test --testPathPattern="src/common"
  1. 测试某个文件
npm run test --testPathPattern="src/common/test.spec.ts"

注意:以 <rootDir> 路径开始,不需要 /

  1. 跳过某个测试用例或分组
// 跳过分组
xdescribe
// 跳过用例
xit
xtest
  1. 使用 vs code 插件 Jest Runner

3, 4, 5 可以使用 Jest Runner 完成。

当前文件空白处 右键 run jest 测试整个文件

某个 describe 上 右键 run jest 测试当前 describe,其他 describe 会自动 xdescribe

某个 it 上 右键 run jest 测试当前 it,其他 describe 会自动 xdescribe,其他 it 会自动 xit

非常好用,如果你使用 jest 强烈推荐

Jest 在 Typescript 中使用配置

从 2017 年开始使用 Typescript,2019 年才开始使用 Jest,以前一直使用 Karma + Jasmine

在配置之前我们需要安装几个依赖:

npm install --save-dev ts-jest @types/jest

Jest 在 Typescript 中使用只需要稍微配置一下:

为了方便灵活,最好还是使用 jest.config.js 文件配置:

const { pathsToModuleNameMapper } = require('ts-jest/utils');
const { compilerOptions } = require('./tsconfig');

module.exports = {
  // 以 <rootDir>/src 这个目录做为根目录来搜索测试文件(模块)
  roots: [
    '<rootDir>/src',
  ],
  // 从下列文件中寻找测试文件
  testMatch: ["**/+(*.)+(spec|test).+(ts|js)?(x)"],
  collectCoverage: false,
  // 收集这些文件的测试覆盖率
  collectCoverageFrom: [
    "src/**/*.{js,jsx,ts,tsx}",
    "!**/node_modules/**",
    "!src/**/*.d.ts"
  ],
  transform: {
    '^.+\\.(ts|js)$': 'ts-jest'
  },
  moduleDirectories: ["node_modules", "/src"],
  // 处理 tsconfig paths 映射路径
  moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths , { prefix: '<rootDir>/' } )
  globals: {
    "ts-jest": {
      // 测试ts配置文件
      tsconfig: '<rootDir>/tsconfig.spec.json',
    },
  },
};

tsconfig.spec.json

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "outDir": "../../dist/out-tsc",
    "module": "commonjs",
    "types": ["jest", "node"]
  },
  "include": ["**/*.spec.ts", "**/*.d.ts"]
}

运行测试脚本:

npm run test

今天就到这里吧,伙计们,玩得开心,祝你好运!

喜欢这篇文章吗?如果是的话,欢迎给我一个 ⭐。