Skip to content

Jest 测试

提示

👉🏻 单元测试: 是指对软件中的最小可测试单元进行检查和验证 👉🏻 集成测试: 在单元测试的基础上,将所有模块按照设计要求组装成为子系统或系统,进行测试

1. 安装

javascript
yarn add -D jest

2. 快速上手

业务逻辑文件 index.js

javascript
// 非我举例,教程举例
function baojian1(money) {
  return money > 200 ? '至尊享受' : '基本按摩'
}

function baojian2(money) {
  return money > 1000 ? '双人服务' : '单人服务'
}

module.exports = { baojian1, baojian2 }

测试文件index.test.js

javascript
// import { baojian1, baojian2 } from './index'
const { baojian1, baojian2 } = require('./index')

test('大保健1--300', () => {
  expect(baojian1(300)).toBe('至尊享受')
})
test('大保健2--2000', () => {
  expect(baojian2(2000)).toBe('双人服务')
})

测试结果:

bash
 PASS  01.第一次测试/index.test.js
 大保健1--300 (2 ms)
 大保健2--2000 (1 ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        0.125 s, estimated 1 s
Ran all test suites.
  Done in 1.29s.

3. 基本配置文件

bash
npx jest --init

 Would you like to use Typescript for the configuration file? no
# 是否使用TypeScript来配置jest配置文件?测试文件非ts选择no
 Choose the test environment that will be used for testing jsdom (browser-like)
# 使用node环境还是浏览器环境来测试
 Do you want Jest to add coverage reports? yes
# 是否希望Jest生成测试报告
 Which provider should be used to instrument code for coverage? v8
# 应该使用哪个提供程序来检测覆盖范围的代码?
 Automatically clear mock calls, instances and results before every test? yes
# 每次测试前自动清除模拟调用、实例和结果?

测试结果:

bash
 PASS  01.第一次测试/index.test.js
 大保健1--300 (1 ms)
 大保健2--2000 (1 ms)

----------|---------|----------|---------|---------|-------------------
| File       | % Stmts   | % Branch   | % Funcs   | % Lines   | Uncovered Line #s   |
| ---------- | --------- | ---------- | --------- | --------- | ------------------- |
| All files  | 100       | 50         | 100       | 100       |
| index.js   | 100       | 50         | 100       | 100       | 3,7                 |
| ---------- | --------- | ---------- | --------- | --------- | ------------------- |
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        0.366 s
Ran all test suites.
  Done in 1.09s.

网页报表:

提示

👉🏻code coverage,代码测试覆盖率,就是我们的测试代码,对功能性代码和业务逻辑代码作了百分多少的测试,这个百分比,就叫做代码测试覆盖率。

4. 匹配器

提示

👉🏻 代码中的undefined, null, and false有不同含义,若你在测试时不想区分他们,可以用真值判断。

提示

👉🏻 对于比较浮点数相等,使用 toBeCloseTo 而不是 toEqual,因为你不希望测试取决于一个小小的舍入误差。

匹配器解释
toBe()等同于===的,也就是我们常说的严格相等。
toEqual()等同于==理解成只要内容相等,就可以通过测试
toBeNul()只匹配null值,需要注意的是不匹配undefined的值。
toBeUndifined()要匹配undefined时,就可以使用,空字符串是没有办法通过测试的
toBeDefined()只要定义过了,都可以匹配成功
toBeTruthy()判断真假。 则通过
toBeFalsy()同义,结果与上相反, 则通过
toBeGreaterThan()只要大于传入的数值,就可以通过测试。
toBeLessThan()同义,结果与上相反
toBeGreaterThanOrEqual()数据大于等于期待数字时,可以通过测试。
toBeLessThanOrEqual()同义,结果与上相反
toBeCloseTo()自动消除 JavaScript 浮点精度错误的匹配器
toMatch()包含指定的字符串,可以通过测试,判断条件可以是字符串也可以是正则
toContain()同义,数组可用
toThrow()对异常进行处理的匹配器,可以检测一个方法会不会抛出异常。
not特殊的匹配器,意思就是相反或者说取反
...更多的匹配器请访问:https://jestjs.io/docs/expect

5.自动监测

每次修改测试用例,我们都手动输入yarn test ,这显得很 lower。可以通过配置package.json文件来设置。修改如下:

javascript
"scripts": {
    "test": "jest --watchAll",
    }

自动监测配置:

bash
Active Filters: filename /./02.匹配器//
 Press c to clear filters.

Watch Usage
 Press f to run only failed tests. # 只运行失败的测试
 Press o to only run tests related to changed files.# 只运行文件内容发生改变的测试
 Press p to filter by a filename regex pattern. # 按文件名正则表达式模式进行筛选
 Press t to filter by a test name regex pattern. # 似乎是按照文件名是否包含test进行筛选
 Press q to quit watch mode.# 退出监测模式
 Press Enter to trigger a test run.# 使用enter 开始测试

6.在 jest 中使用 import

目前我们的 Jest 是不支持import...from....这种形式,如果使用就会报错,因为 Jest 默认支持的是CommonJS规范,也就是Node.js中的语法,他只支持require这种引用。所以我们使用import...from...是 ES6 的语法,所以使用就会报错。

解决方案:

6.1 安装 Babel 转换器

其实解决这个问题,直接使用 Babel 就可以把代码转换成CommonJS代码,然后就可以顺利进行测试了。那现在就来安装一下Babel.

javascript
yarn add -D @babel/core@7.4.5 @babel/preset-env@7.4.5

6.2 配置.babelrc 文件

我们在项目根目录下新建一个.babelrc的文件,作为一个前端,你对这个文件应该是非常熟悉的,babel 的转换配置就写在这个文件里。

javascript
{
    "presets":[
        [
                "@babel/preset-env",{
                "targets":{
                    "node":"current"
                }
            }
        ]
    ]
}

当你写完这段代码后,就可以进行转换了。我们再次使用yarn test进行测试,这时候就可以正确通过测试了。也就是说我们用的babel起到作用了。

那为什么会这样那?其实在Jest里有一个babel-jest组件,我们在使用yarn test的时候,它先去检测开发环境中是否安装了babel,也就是查看有没有babel-core,如果有bable-core就会去查看.babelrc配置文件,根据配置文件进行转换,转换完成,再进行测试。

7. 测试异步函数

需要前置axios 模块

bash
yarn add axios

7.1 普通异步

新建一个文件index.js文件,然后编写代码如下:

javascript
import axios from 'axios'
const URL = 'http://39.98.123.211/api/user/passport/login'

export function getData(callback) {
  axios.get(URL).then(response => callback(response))
}

这是我们最经常使用的一种异步请求方法的形式,接着新建一个index.test.js的测试文件,编写代码如下:

javascript
import { getData } from './index'

test('异步测试', done => {
  getData(data => {
    try {
      const _data = JSON.stringify(data)
      // TODO: 接口返回的是json,只能暂时先转换为字符串然后匹配,看后期有什么优化的空间
      expect(_data).toMatch('"status":200')
      done()
    } catch (err) {
      done(err)
    }
  })
})

7.2 测试返回结果为 Promise 对象

有时候会直接返回一个promise值,这样的方法在工作中也式经常使用的。

javascript
// 被测试单元
import axios from 'axios'
const URL = 'http://39.98.123.211/api/user/passport/login'

export const getDataTwo = () => {
  return axios.get(URL)
}

测试代码:

javascript
import { getData, getDataTwo } from './index'

test('Promise测试', () => {
  return getDataTwo().then(res => {
    // const _res = JSON.stringify(res)
    expect(res).toMatchObject({ status: 200 })
    console.log('测试通过')
  })
})

7.3 指定测试结果状态

在特殊需求中,需要将原本不通过测试的状态改为通过

javascript
// 被测试单元
import axios from 'axios'
const URL = 'http://39.98.123.211/api/user/passport/login'

export const getDataTwo = () => {
  return axios.get(URL)
}

测试代码:

javascript
import { getData, getDataTwo } from './index'

test('Promise测试捕获异常,使其通过测试', () => {
  expect.assertions(1) // 断言,使其必须走一次测试流程,如果结果正常,则会报错,因为这是专门测试异常的测试条件
  return getDataTwo().catch(err => {
    // console.log(err.toString())
    expect(err.toString().includes('404')).toBeTruthy()
  })
})

7.4 对 async ... aswit 测试

javascript
// 被测试文件
import axios from 'axios'
const URL = 'http://39.98.123.211/api/user/passport/login'

export const getDataTwo = () => {
  return axios.get(URL)
}

测试文件:

javascript
test('asyc...await测试', async () => {
  const res = await getDataTwo()
  expect(res).toMatchObject({ status: 200 })
  console.log('asyc...await测试通过')
})