从零开始学前端自动化测试(一) jest快速入门

480 阅读5分钟

快速入门

环境检测

D:\Vue-Automated-Testing>node -v
v12.13.0

D:\Vue-Automated-Testing>npm -v
6.12.0

初始化项目, 生成 package.json 文件, 可以一路回车

npm init

安装 jest 使用 yarn 或使用 npm 安装

yarn add --dev jest
npm install --save-dev jest

Jest的文档统一使用 yarn 指令, 但是用 npm 同样可行, 可以通过 yarn官方文档 进行 yarn 和 npm 对比

为了便于文件管理, 创建三个文件夹

├─server (服务器代码)
├─src (逻辑代码)
├─test (测试代码)
package.json (包管理器)

Hello world!

src 文件夹下创建 index.js 文件

export function test() {
  return 'Hello World!'
}

然后在 test 文件夹下创建 index.test.js 测试文件

import { hello } from '../src/index'

test('Hello World!', () => {
  escape(hello()).toBe('Hello World!')
})

由于使用了 import ES6 的语法, 故而要使用 Babel

通过 npm 安装依赖

npm install @babel/core@7.4.5 @babel/preset-env@7.4.5 -D

在项目的根目录下创建 babel.config.js ,通过配置 Babel 使其能够兼容当前的 Node 版本。

module.exports = {
  presets: [
    [
      '@babel/preset-env',
      {
        targets: {
          node: 'current',
        },
      },
    ],
  ],
};

然后配置脚本命令, 将如下代码添加到 package.json 中:

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

最后,运行 yarn test 或者 npm run test,Jest 将输出如下信息:

 PASS  test/index.test.js
  √ Hello World! (1 ms)

喜大普奔, 我们已经成功写好了第一个测试用例了

然后我们看下测试到底干了什么

每个 test 方法都是一个小模块, 它接受两个参数, 第一个参数是该模块的测试描述, 第二个参数是一个函数, 是你所需要的执行的测试用例

这个测试用例很简单, 每次要测试一个值时都会使用 expect 函数。它接收一个值, expect 函数后往往会跟一个"匹配器", 已验证 expect 函数里面的值, 这里使用的是 toBe 匹配器, 意思是 匹配器内的值与 expect 函数里的值严格等于

匹配器

常用匹配器

jest 为我们提供一些常用的匹配器

匹配器说明
toBe严格等于
toEqual匹配值
toBeNull匹配 Null
toBeUndefined匹配 undefined
toBeDefined匹配定义
toBeTruthy匹配真值
toBeFalsy匹配假值
toBeGreaterThan大于
toBeGreaterThanOrEqual大于等于
toBeLessThan小于
toBeLessThanOrEqual小于等于
toBeCloseTo浮点问题
toMatch用来判断一个字符串是否包含一个指定的值
toContain用来判断一个数组(Set 对象)是否包含一个指定的值
not取反
toThrow匹配异常

更多匹配器

要获得匹配器的完整列表,请查看 参考文档

测试方法

下面我们开始为一个函数写测试,这个函数的功能是解析时间对象为字符串。首先创建 utils.js 文件:

/**
 * Parse the time to string
 * @param {(Object|string|number)} time
 * @param {string} cFormat
 * @returns {string}
 */
export function parseTime(time, cFormat) {
  if (arguments.length === 0) {
    return null
  }
  const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
  let date
  if (typeof time === 'object') {
    date = time
  } else {
    if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
      time = parseInt(time)
    }
    if ((typeof time === 'number') && (time.toString().length === 10)) {
      time = time * 1000
    }
    date = new Date(time)
  }
  const formatObj = {
    y: date.getFullYear(),
    m: date.getMonth() + 1,
    d: date.getDate(),
    h: date.getHours(),
    i: date.getMinutes(),
    s: date.getSeconds(),
    a: date.getDay()
  }
  const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
    let value = formatObj[key]
    // Note: getDay() returns 0 on Sunday
    if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value ] }
    if (result.length > 0 && value < 10) {
      value = '0' + value
    }
    return value || 0
  })
  return time_str
}

接下来, 创建名为 utils.test.js 的文件。这个文件包含了实际的测试内容:

const utils = require('../src/utils')
const { parseTime } = utils

test('测试方法: parseTime', () => {
  const d = new Date('2020-10-19 11:45:01') // 2020-10-19 11:45:01
  expect(parseTime(d)).toBe('2020-10-19 11:45:01')
  expect(parseTime(d, '{y}-{m}-{d} {h}:{i}')).toBe('2020-10-19 11:45')
  expect(parseTime(d, '{y}年{m}月{d}日')).toBe('2020年10月19日')
  expect(parseTime(d, '{a}')).toBe('一')
})

最后,运行 yarn test 或者 npm run test,Jest 将输出如下信息:

PASS  test/utils.test.js
✓ Utils:parseTime (4ms)

测试通过了, 但却不是一个合格的测试用例, 要想写一个合格的测试用例, 我们必须要了解什么是测试覆盖率, 再了解这个之前我们先来一些基础配置

基础配置

执行命令初始化文件 jest.config.js

npx jest --init

目前我们只需了解一个配置(生成覆盖率文件)

module.exports = {
  // The directory where Jest should output its coverage files
  coverageDirectory: "coverage"
}

然后我们在 package.json 文件中继续添加一行

{
  "script": {
    "coverage": "npx jest --coverage"
  }
}

然后运行

npm run coverage

测试覆盖率

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files |      70 |       50 |     100 |   68.42 |                  
 index.js |     100 |      100 |     100 |     100 |                  
 utils.js |   68.42 |       50 |     100 |   66.67 | 9,16-22          
----------|---------|----------|---------|---------|-------------------

参数表说明:

  • Stmts 语句覆盖率 每个语句是否都执行了
  • Branch 分支覆盖率 条件语句是否都执行了
  • Funcs 函数覆盖率 函数是否全部调用了
  • Lines 行覆盖率 未执行的行数

说明还是有些地方并未测试到

修改代码

describe('测试方法:parseTime', () => {
  const d = new Date('2018-07-13 17:54:01') // "2018-07-13 17:54:01"
  it('timestamp', () => {
    expect(parseTime(d)).toBe('2018-07-13 17:54:01')
  })
  it('ten digits timestamp', () => {
    expect(parseTime((d / 1000).toFixed(0))).toBe('2018-07-13 17:54:01')
  })
  it('new Date', () => {
    expect(parseTime(new Date(d))).toBe('2018-07-13 17:54:01')
  })
  it('format', () => {
    expect(parseTime(d, '{y}-{m}-{d} {h}:{i}')).toBe('2018-07-13 17:54')
    expect(parseTime(d, '{y}-{m}-{d}')).toBe('2018-07-13')
    expect(parseTime(d, '{y}/{m}/{d} {h}-{i}')).toBe('2018/07/13 17-54')
  })
  it('get the day of the week', () => {
    expect(parseTime(d, '{a}')).toBe('五') // 星期五
  })
  it('get the day of the week', () => {
    expect(parseTime(+d + 1000 * 60 * 60 * 24 * 2, '{a}')).toBe('日') // 星期日
  })
  it('empty argument', () => {
    expect(parseTime()).toBeNull()
  })
})

测试结果

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files |     100 |    95.45 |     100 |     100 |                  
 index.js |     100 |      100 |     100 |     100 |                  
 utils.js |     100 |    95.45 |     100 |     100 | 40               
----------|---------|----------|---------|---------|-------------------

语句覆盖率和行覆盖率都打到 100%, 说明测试是合格的

Github 地址