Skip to content

Jest 简单使用

Jest 是一个 JavaScript 测试框架,是目前比较热门使用的测试框架之一。

中文文档

安装

bash
npm install -D jest

如果你要生成 Jest 的基础配置文件,执行👇

bash
npx jest --init

如果你要生成 Jest 的代码测试覆盖率,执行👇

bash
npx jest --coverage

使用

首先新建一个 demo01.js,编写如下代码:

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

然后再新建一个测试文件 demo01.test.js,编写如下代码:

javascript
const sum = require('./sum')
test('sum方法测试', () => {
  expect(sum(1, 2)).toBe(3)
})

package.json 加入

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

最后在控制台输入 npm run test

这时控制台会打印一条 PASS ./demo01.test.js。说明测试通过。

好了,上面这个小例子就是 Jest 的基本用法。

正文现在才开始😉😉😉

demo01.test.js 中我们看到有一个 test() 的方法,它是用来测试的,相当于一个测试用例。

test() 第一个参数是测试的描述,第二个参数是一个函数体,允许我们在函数体内进行编写测试代码

expect(value) 是每次测试一个值的时候都使用的函数,通常与匹配器一起使用,例如 demo01.test.js 中的 toBe(),它相当于 ===

这时候我们回头看看之前 demo01.test.js 的代码,应该就不难理解了

测试描述:sum方法测试

测试 sum(1, 2) 这个方法并传入参数1, 2,期望的值是否 === 3。

然后输出的结果是 PASS 表示 测试通过💯

常用的匹配器

  • toBe(value) 相当于 ===

  • toEqual(value) 相当于 ==

  • toBeNull() 判断是否为null

  • toBeUndefined() 判断是否为undefined

  • toBeDefined() 判断是否已定义,即非null or undefined

  • toBeTruthy() 判断是否为 真值

  • toBeFalsy() 判断是否为 假值

  • toBeGreaterThan(value) 判断是否大于 value

  • toBeGreaterThanOrEqual(value) 判断是否大于等于 value

  • toBeLessThan(value) 判断是否小于 value

  • toBeLessThanOrEqual(value) 判断是否小于等于 value

  • toBeCloseTo(value) 解决数字类型精度问题的匹配

  • toMatch(value) 判断字符串是否包含 value

  • toContain(value) 判断数组、Set是否包含 value

  • toThrow([value]) 判断是否抛出异常,抛出则通过,反之不通过。若传入value则会判断抛出的异常是否与value一致。

  • not 取反,一般配合其他匹配器使用,如not.toThrow() 表示不抛出异常。建议能直接使用匹配器的就直接使用,被迫才使用它来搭配。

javascript
// 部分匹配器 实例:
test('测试匹配', () => {
  const a = {num: '007'}
  expect(a).toBe(a)
}) // PASS

test('toEqual匹配', () => {
  const a = {num: '007'}
  expect(a).toEqual({num: '007'})
}) // PASS

// 注意:若这里使用toBe(),按数学而言0.1+0.2 === 0.3 这是没有问题的,但在Javascript中,因为设计问题,0.1+0.2 === 0.30000004,因此是 !== 0.3 的,所有这里使用 toBeCloseTo() 解决精度问题才能正确测试。
test('toBeCloseTo匹配', () => {
  expect(0.1 + 0.2).toBeCloseTo(0.3)
}) // PASS

四个构子

  • beforeAll(fn, timeout) 执行在所有测试用例之前,timeout为超时时间。

  • afterAll(fn, timeout) 执行在所有测试用例之后

  • beforeEach(fn, timeout) 在每个测试用例执行之前都执行

  • afterEach(fn, timeout) 在每个测试用例执行之后都执行

作用域

钩子函数在父级分组可作用域子集,类似继承。

钩子函数同级分组作用域互不干扰,各起作用。

先执行外部的钩子函数,再执行内部的钩子函数。

注意

一个测试文件里,就相当于默认有一个分组,只是省略不见而已。

describe(name, fn) 分组,第一个参数分组名字,第二个参数是一个函数体,可以添加多个测试用例,即test()

javascript
// 构子 实例
beforeAll(() => {
  console.log('执行在所有测试用例之前')
})

afterAll(() => {
  console.log('执行在所有测试用例之后')
})

beforeEach(() => {
  console.log('在每个测试用例执行之前都执行')
})

afterEach(() => {
  console.log('在每个测试用例执行之后都执行')
})

test('测试1', () => {
  console.log('开始测试1')
  expect(sum(1, 2)).toBe(3)
})

test('测试2', () => {
  console.log('开始测试2');
  expect(sum(3, 3)).toBe(6)
})

jest

javascript
// 作用域 实例
beforeAll(() => {
  console.log('我是父组beforeAll')
})

afterAll(() => {
  console.log('我是父组afterAll')
})

describe('这是分组1', () => {
  beforeAll(() => {
    console.log('我是分组1 beforeAll')
  })
  
  afterAll(() => {
    console.log('我是分组1 afterAll')
  })

  test('测试1', () => {
    console.log('分组1-测试1')
  })

  test('测试2', () => {
    console.log('分组1-测试2')

  })
})

describe('这是分组2', () => {
  beforeAll(() => {
    console.log('我是分组2 beforeAll')
  })
  
  afterAll(() => {
    console.log('我是分组2 afterAll')
  })

  test('测试1', () => {
    console.log('分组2-测试1')
  })

  test('测试2', () => {
    console.log('分组2-测试2')
  })
})

jest

‼️坑点

细心的人会发现,在我们编写的 demo01.jsdemo01.test.js 文件中,我们都是使用 commonJs 语法(对于还不知道 commonJs 语法是什么的,自行百度,这里就不多说了),但现在基本前端项目都是使用了 Es module 语法。这是 Jest 不支持的

但可以使用 Babel 进行语法转换,首先安装 Babel👇

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

然后在项目根目录新建babel 配置文件 .babelrc

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

这时您就可以放心使用 Es module 语法啦~~~

异步代码测试

1️⃣第一个坑点:异步回调测试

默认jest 只把代码能执行就表示为通过了,即不会等异步代码执行完毕,但我们可以通过传入回调中的 done参数,来表示何时检查结束。类似新版gulp语法。

javascript
// demo02.js
function fetchXxx(fn) {
	fetch('http://xxx.json').then(res => {
        fn(res)
    })
}
javascript
// demo02.test.js

// 错误写法
test('fetchXxx 测试', () => {
    fetchXxx(data => {
        expect(data).toEqual({
            success: true
        })
    })
}) // PASS
// 最后会输出 pass,这不就是对了吗?不,它是无论什么都会测试通过,你可以尝试把 请求地址 改为错误的,它一样 PASS,因为它这里不会等待请求回来(即异步处理完毕),它只有能把 请求 发出去了就表示测试通过了,这很明显不符合我们想要的测试需求。因此我们可以在 test() 方法的 第二个参数 fn 中传入 done,在我们想要测试的结束处执行 done() 即可。 如下

// 正确写法
test('fetchXxx 测试', done => {
    fetchXxx(data => {
        expect(data).toEqual({
            success: true
        })
        done()
    })
}) // PASS

2️⃣第二个坑点:返回一个未处理的 promise

javascript
// demo03.js
function fetchXxx() {
	return fetch('http://xxx.json')
}
javascript
// demo03.test.js

// 错误写法
test('fetchXxx 测试', () => {
  fetchXxx().then(data => {
    expect(data).toEqual({
      success: true
    })
  })
}) // PASS,原因与第一个坑点一样,无论何时都是 PASS

// 正确写法
test('fetchXxx 测试', () => {
  return fetchXxx().then(data => {
    expect(data).toEqual({
      success: true
    })
  })
})

// PS: 可以不使用 return, 也可以使用 async/await

3️⃣第三个坑点:异步捕获异常

javascript
// demo04.js
function fetchXxx() {
	return fetch('http://xxx.json')
}
javascript
// demo04.test.js

// 错误写法
test('fetchXxx 测试', () => {
  return fetchXxx().catch(e => {
    expect(e.toString().indexOf('404') > -1).toBe(true)
  })
}) // PASS
// 看似没有问题, return 也做了,但实际上它也是无论什么时候都返回 PASS。

// 正确写法
test('fetchXxx 测试', () => {
  expect.assertions(1) // 若没有这句,无论是否有异常都表示测试通过
  return fetchXxx().catch(e => {
    expect(e.toString().indexOf('404') > -1).toBe(true)
  })
}) // PASS
// expect.assertions(value) 表示断言,value表示 expect至少要执行多少次

简单使用 Jest 就到此结束了,学会了吗???🤨🤨🤨

Released under the MIT License.