用Jest测试JavaScript

Jest是用于测试JavaScript代码的库。这是Facebook维护的一个开源项目,它特别适合于React代码测试,尽管不仅限于此:它可以测试任何JavaScript代码。笑话非常快速且易于使用

笑话简介

Jest是用于测试JavaScript代码的库。

这是Facebook维护的一个开源项目,它特别适合于React代码测试,尽管不仅限于此:它可以测试任何JavaScript代码。它的优势是:

  • 它很快
  • 它可以执行快照测试
  • 固执己见,并提供所有现成的内容,而无需您做出选择

Jest是与Mocha非常相似的工具,尽管它们之间存在差异:

  • 摩卡(Mocha)固执己见,而杰斯特(Jest)有一些约定
  • Mocha需要更多的配置,而Jest通常开箱即用,这要归功于他们自以为是
  • 摩卡(Mocha)更古老,更成熟,具有更多的工具集成

在我看来,Jest的最大特点是它是一种开箱即用的解决方案,无需与其他测试库进行交互即可执行其工作。

安装

Jest会自动安装在create-react-app,因此,如果您使用它,则无需安装Jest。

可以使用以下命令将Jest安装在任何其他项目中

yarn add --dev jest

或者npm

npm install --save-dev jest

请注意,我们如何指示双方将玩笑放在devDependencies的一部分package.json文件,以便仅将其安装在开发环境中,而不能在生产环境中安装。

将此行添加到您的脚本部分package.json文件:

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

这样就可以使用yarn test或者npm run test

或者,您可以全局安装Jest:

yarn global add jest

然后使用jest命令行工具。

创建第一个Jest测试

创建的项目create-react-app开箱即用地安装并预先配置了Jest,但是将Jest添加到任何项目就像键入一样容易

yarn add --dev jest

加到你的package.json这行:

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

并通过执行运行测试yarn test在你的壳里。

现在,您在这里没有任何测试,因此将不执行任何操作:

Testing with Yarn

让我们创建第一个测试。打开一个math.js文件并输入几个函数,我们稍后将对其进行测试:

const sum = (a, b) => a + b
const mul = (a, b) => a * b
const sub = (a, b) => a - b
const div = (a, b) => a / b

module.exports = { sum, mul, sub, div }

现在创建一个math.test.js文件,放在同一文件夹中,我们将在其中使用Jest测试在中定义的功能math.js

const { sum, mul, sub, div } = require('./math')

test(‘Adding 1 + 1 equals 2’, () => { expect(sum(1, 1)).toBe(2) }) test(‘Multiplying 1 * 1 equals 1’, () => { expect(mul(1, 1)).toBe(1) }) test(‘Subtracting 1 - 1 equals 0’, () => { expect(sub(1, 1)).toBe(0) }) test(‘Dividing 1 / 1 equals 1’, () => { expect(div(1, 1)).toBe(1) })

跑步yarn test导致Jest在找到的所有测试文件上运行,并向我们返回最终结果:

Passing tests

用VS Code运行Jest

Visual Studio Code是JavaScript开发的出色编辑器。这玩笑扩展为我们的测试提供了一流的集成。

安装后,它将自动检测是否已在devDependencies中安装了Jest并运行测试。您还可以通过选择开玩笑:亚军命令。每当您更改具有测试的文件之一(或测试文件)时,它将运行测试并停留在监视模式下以重新运行它们:

A simple Jest test running in VS Code

匹配器

在上一篇文章中,我使用了toBe()作为唯一的匹配器

test('Adding 1 + 1 equals 2', () => {
  expect(sum(1, 1)).toBe(2)
})

匹配器是一种允许您测试值的方法。

最常用的匹配器,比较结果的值expect()使用作为参数传递的值是:

  • toBe比较严格平等,使用===
  • toEqual比较两个变量的值。如果是对象或数组,则检查所有属性或元素的相等性
  • toBeNull传递空值时为true
  • toBeDefined传递定义的值时为true(与上述相反)
  • toBeUndefined传递未定义的值时为true
  • toBeCloseTo用于比较浮点值,避免舍入错误
  • toBeTruthy如果该值被视为true,则返回true(例如if做)
  • toBeFalsy如果该值被认为是假的,则返回true(例如if做)
  • toBeGreaterThan如果Expect()的结果高于参数,则为true
  • toBeGreaterThanOrEqual如果Expect()的结果等于参数,或者高于参数,则为true
  • toBeLessThan如果Expect()的结果小于参数,则为true
  • toBeLessThanOrEqual如果Expect()的结果等于参数,或者小于参数,则为true
  • toMatch用于比较字符串正则表达式模式匹配
  • toContain用于数组中,如果期望的数组在其元素集中包含参数,则为true
  • toHaveLength(number):检查数组的长度
  • toHaveProperty(key, value):检查对象是否具有属性,并可选地检查其值
  • toThrow检查您传递的函数是否抛出异常(通常)或特定异常
  • toBeInstanceOf():检查对象是否是类的实例

所有这些匹配器都可以使用.not.在语句中,例如:

test('Adding 1 + 1 does not equal 3', () => {
  expect(sum(1, 1)).not.toBe(3)
})

与诺言一起使用,您可以使用.resolves.rejects

expect(Promise.resolve('lemon')).resolves.toBe('lemon')

expect(Promise.reject(new Error(‘octopus’))).rejects.toThrow(‘octopus’)

设置

在运行测试之前,您将需要执行一些初始化。

要在所有测试运行之前执行一次操作,请使用beforeAll()功能:

beforeAll(() => {
  //do something
})

要在每次测试运行之前执行某些操作,请使用beforeEach()

beforeEach(() => {
  //do something
})

拆除

就像您可以进行设置一样,您还可以在每次测试运行后执行一些操作:

afterEach(() => {
  //do something
})

在所有测试结束后:

afterAll(() => {
  //do something
})

使用describe()进行组测试

您可以在单个文件中创建测试组,以隔离设置和拆卸功能:

describe('first set', () => {
  beforeEach(() => {
    //do something
  })
  afterAll(() => {
    //do something
  })
  test(/*...*/)
  test(/*...*/)
})

describe(‘second set’, () => { beforeEach(() => { //do something }) beforeAll(() => { //do something }) test(//) test(//) })

测试异步代码

现代JavaScript中的异步代码基本上可以有两种形式:回调和Promise。除了承诺,我们还可以使用async / await。

回呼

您不能在回调中进行测试,因为Jest不会执行测试-测试文件的执行在调用回调之前结束。要解决此问题,请将参数传递给测试函数,您可以方便地调用它done。开玩笑会等到你打电话done()在结束测试之前:

//uppercase.js
function uppercase(str, callback) {
  callback(str.toUpperCase())
}
module.exports = uppercase

//uppercase.test.js const uppercase = require(’./src/uppercase’)

test(uppercase 'test' to equal 'TEST', (done) => { uppercase(‘test’, (str) => { expect(str).toBe(‘TEST’) done() } })

Jest async test callback

承诺

通过返回承诺的功能,我们兑现承诺从测试:

//uppercase.js
const uppercase = str => {
  return new Promise((resolve, reject) => {
    if (!str) {
      reject('Empty string')
      return
    }
    resolve(str.toUpperCase())
  })
}
module.exports = uppercase

//uppercase.test.js const uppercase = require(’./uppercase’) test(uppercase 'test' to equal 'TEST', () => { return uppercase(‘test’).then(str => { expect(str).toBe(‘TEST’) }) })

Jest async test promises

可以用以下方法测试被拒绝的承诺.catch()

//uppercase.js
const uppercase = str => {
  return new Promise((resolve, reject) => {
    if (!str) {
      reject('Empty string')
      return
    }
    resolve(str.toUpperCase())
  })
}

module.exports = uppercase

//uppercase.test.js const uppercase = require(’./uppercase’)

test(uppercase 'test' to equal 'TEST', () => { return uppercase(’’).catch(e => { expect(e).toMatch(‘Empty string’) }) })

Jest async test catch

异步/等待

为了测试返回promise的函数,我们还可以使用async / await,这使得语法非常简单明了:

//uppercase.test.js
const uppercase = require('./uppercase')
test(`uppercase 'test' to equal 'TEST'`, async () => {
  const str = await uppercase('test')
  expect(str).toBe('TEST')
})

Jest async test await async

模拟

在测试中嘲笑允许您测试依赖于以下功能的功能:

  • 数据库
  • 网络要求
  • 进入档案文件
  • 任何外部的系统

以便:

  1. 您的测试运行快点,在开发过程中提供了快速的周转时间
  2. 你的测试是独立的网络条件或数据库状态
  3. 您的测试不污染任何数据存储,因为它们不接触数据库
  4. 测试中所做的任何更改都不会更改后续测试的状态,并且重新运行测试套件应从已知且可复制的起点开始
  5. 您不必担心API调用和网络请求的速率限制

当您想要避免副作用(例如,写入数据库)或想要跳过代码的慢速部分(例如,网络访问),并且还可以避免多次运行测试所带来的影响(例如,假设某个函数发送一个错误的代码)时,模拟非常有用。电子邮件或调用受速率限制的API)。

更重要的是,如果您正在编写单元测试,您应该单独测试功能的功能,而不是对其所有涉及的事物进行测试。

使用模拟,您可以通过以下方法检查是否已调用模块函数以及使用了哪些参数:

  • expect().toHaveBeenCalled():检查是否已调用间谍函数
  • expect().toHaveBeenCalledTimes():计算一个间谍函数被调用了多少次
  • expect().toHaveBeenCalledWith():检查是否已使用一组特定的参数调用了该函数
  • expect().toHaveBeenLastCalledWith():检查上次调用该函数的参数

间谍软件包而不会影响功能代码

导入软件包时,您可以使用以下命令告诉Jest对特定功能的执行“间谍”:spyOn(),而不会影响该方法的工作方式。

例子:

const mathjs = require('mathjs')

test(The mathjs log function, () => { const spy = jest.spyOn(mathjs, ‘log’) const result = mathjs.log(10000, 10)

expect(mathjs.log).toHaveBeenCalled() expect(mathjs.log).toHaveBeenCalledWith(10000, 10) })

模拟整个包裹

Jest提供了一种模拟整个程序包的便捷方法。创建一个__mocks__文件夹在项目根目录中,并在此文件夹中为每个包创建一个JavaScript文件。

说你汇入mathjs。创建一个__mocks__/mathjs.js文件放在您的项目根目录中,并添加以下内容:

module.exports = {
  log: jest.fn(() => 'test')
}

这将模拟包的log()函数。添加任意数量的要模拟的函数:

const mathjs = require('mathjs')

test(The mathjs log function, () => { const result = mathjs.log(10000, 10) expect(result).toBe(‘test’) expect(mathjs.log).toHaveBeenCalled() expect(mathjs.log).toHaveBeenCalledWith(10000, 10) })

模拟一个功能

您可以使用以下方法模拟单个函数jest.fn()

const mathjs = require('mathjs')

mathjs.log = jest.fn(() => ‘test’) test(The mathjs log function, () => { const result = mathjs.log(10000, 10) expect(result).toBe(‘test’) expect(mathjs.log).toHaveBeenCalled() expect(mathjs.log).toHaveBeenCalledWith(10000, 10) })

您也可以使用jest.fn().mockReturnValue('test')创建一个简单的模拟,除了返回值外不执行任何操作。

预先建立的模拟

您可以找到流行库的预制模型。例如这个包https://github.com/jefflau/jest-fetch-mock让你嘲笑fetch()调用,并提供示例返回值,而无需与测试中的实际服务器进行交互。

快照测试

快照测试是Jest提供的一项很酷的功能。它可以记住UI组件的呈现方式,并将其与当前测试进行比较,如果不匹配,则会引发错误。

这是对简单的App组件的简单测试create-react-app应用程序(确保已安装react-test-renderer):

import React from 'react'
import App from './App'
import renderer from 'react-test-renderer'

it(‘renders correctly’, () => { const tree = renderer.create(<App />).toJSON() expect(tree).toMatchSnapshot() })

第一次运行此测试时,Jest将快照保存到__snapshots__文件夹。这是App.test.js.snap包含的内容:

// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`renders correctly 1`] = `
<div
  className="App"
>
  <header
    className="App-header"
  >
    <img
      alt="logo"
      className="App-logo"
      src="logo.svg"
    />
    <h1
      className="App-title"
    >
      Welcome to React
    </h1>
  </header>
  <p
    className="App-intro"
  >
    To get started, edit
    <code>
      src/App.js
    </code>
     and save to reload.
  </p>
</div>
`

如您所见,这是App组件呈现的代码,仅此而已。

下次测试比较输出<App />对此。如果应用程序发生更改,则会出现错误:

Error with snapshot

使用时yarn testcreate-react-app你在观看模式,然后您可以按w并显示更多选项:

Watch Usage
 › Press u to update failing snapshots.
 › Press p to filter by a filename regex pattern.
 › Press t to filter by a test name regex pattern.
 › Press q to quit watch mode.
 › Press Enter to trigger a test run.

If your change is intended, pressing u will update the failing snapshots, and make the test pass.

You can also update the snapshot by running jest -u (or jest --updateSnapshot) outside of watch mode.


More devtools tutorials: