使用Jest对JavaScript进行测试
Jest是一个用于测试JavaScript代码的库。它是一个由Facebook维护的开源项目,特别适用于React代码的测试,尽管不仅限于此:它可以测试任何JavaScript代码。Jest非常快速和易于使用。
Jest简介
Jest是一个用于测试JavaScript代码的库。
它是一个由Facebook维护的开源项目,特别适用于React代码的测试,尽管不仅限于此:它可以测试任何JavaScript代码。它的优势是:
- 快速
- 可以执行快照测试
- 它具有某些约定,并且提供了一切必要的东西,无需进行选择
Jest是一个和Mocha非常相似的工具,尽管它们之间存在一些差异:
- Mocha更灵活,而Jest有一套规则
- Mocha需要更多的配置,而Jest通常可以直接运行,因为它有一套规则
- Mocha更早并且更稳定,拥有更多的工具集成
在我看来,Jest最重要的特点是它是一个开箱即用的解决方案,无需与其他测试库进行交互即可完成工作。
安装
在create-react-app
中,默认安装了Jest,所以如果你使用它,你不需要安装Jest。
使用Yarn可以在其他项目中安装Jest:
1 | yarn add --dev jest |
或者使用npm:
1 | npm install --save-dev jest |
请注意,我们都将Jest放在了package.json
文件的devDependencies
部分中,这样它将仅在开发环境中安装,而不会安装在生产环境中。
将以下代码添加到package.json
文件的scripts部分中:
1 | { |
这样我们就可以使用yarn test
或npm run test
来运行测试。
或者,您还可以全局安装Jest:
1 | yarn global add jest |
并使用jest
命令行工具运行所有测试。
创建第一个Jest测试
使用create-react-app
创建的项目已经默认安装并预配置了Jest,但是将Jest添加到任何项目中都非常简单,只需键入以下命令即可:
1 | yarn add --dev jest |
将以下代码添加到您的package.json
文件中:
1 | { |
然后通过在shell中执行yarn test
来运行测试。
现在,您还没有任何测试,因此不会执行任何操作:
让我们创建第一个测试。打开一个math.js
文件并键入我们将稍后测试的一些函数:
1 | const sum = (a, b) => a + b |
现在,在相同的文件夹中创建一个math.test.js
文件,并在其中使用Jest来测试在math.js
中定义的函数:
1 | const { sum, mul, sub, div } = require('./math') |
运行yarn test
将在所有找到的测试文件上运行Jest,并返回结果:
使用VS Code运行Jest
VS Code是JavaScript开发的一个很好的编辑器。Jest扩展为我们的测试提供了一流的集成。
安装后,它会自动检测您是否已经在devDependencies中安装了Jest,并运行测试。您也可以通过选择Jest: Start Runner命令来手动执行测试。它将运行测试并保持在监视模式下,以在更改了具有测试(或测试文件)的文件之一时重新运行它们:
匹配器
在前一篇文章中,我只使用了toBe()
作为唯一的匹配器:
1 | test('Adding 1 + 1 equals 2', () => { |
匹配器是一种方法,用于测试值。
最常用的匹配器是将expect()
的结果与传递的值进行比较:
toBe
使用===
进行严格的相等比较toEqual
比较两个变量的值。如果它是对象或数组,则检查所有属性或元素的等式toBeNull
在传递null值时为truetoBeDefined
在传递一个已定义的值时为true(与上述相反)toBeUndefined
在传递一个未定义的值时为truetoBeCloseTo
用于比较浮点值,避免四舍五入误差toBeTruthy
如果值被视为真(例如if
语句)则为truetoBeFalsy
如果值被视为假(例如if
语句)则为truetoBeGreaterThan
如果expect()的结果大于参数,则为truetoBeGreaterThanOrEqual
如果expect()的结果等于参数或大于参数,则为truetoBeLessThan
如果expect()的结果小于参数,则为truetoBeLessThanOrEqual
如果expect()的结果等于参数或小于参数,则为truetoMatch
用于使用正则表达式模式匹配字符串toContain
用于数组,在其元素集合中包含参数时为truetoHaveLength(number)
:检查数组的长度toHaveProperty(key, value)
:检查对象是否具有属性,并可选地检查其值toThrow
检查您传递的函数是否抛出了异常(一般或特定的异常)toBeInstanceOf()
:检查对象是否为类的实例
所有这些匹配器都可以使用语句中的.not.
进行否定,例如:
1 | test('Adding 1 + 1 does not equal 3', () => { |
要与Promise一起使用,可以使用.resolves
和.rejects
:
1 | expect(Promise.resolve('lemon')).resolves.toBe('lemon') |
设置
在运行测试之前,您需要进行一些初始化工作。
使用beforeAll()
函数可以在运行所有测试之前执行一次操作:
1 | beforeAll(() => { |
使用beforeEach()
可以在每个测试运行之前执行一些操作:
1 | beforeEach(() => { |
清除
与设置类似,您还可以在每个测试运行之后执行一些操作:
1 | afterEach(() => { |
在所有测试结束后执行操作:
1 | afterAll(() => { |
使用describe()分组测试
您可以在一个单独的文件中创建测试组,以隔离设置和清除操作:
1 | describe('第一组', () => { |
测试异步代码
在现代JavaScript中,异步代码有两种形式:回调函数和Promises。在Promises的基础上,我们可以使用async/await。
回调函数
您不能在回调函数中进行测试,因为Jest不会执行它 - 在调用回调函数之前,测试文件的执行就结束了。要解决这个问题,请将一个参数传递给测试函数,并方便地命名为done
。在结束该测试之前,Jest将等待您调用done()
:
1 | //uppercase.js |
Promises
对于返回Promises的函数,我们需要从测试中返回一个Promise:
1 | //uppercase.js |
被拒绝的Promises则可以使用.catch()
进行测试:
1 | //uppercase.js |
Async/await
对于返回Promises的函数,我们还可以使用async/await,从而使语法非常简洁明了:
1 | //uppercase.test.js |
模拟
在测试中,模拟允许您测试依赖于:
- 数据库
- 网络请求
- 访问文件
- 任何外部系统
这样做的好处是:
- 您的测试运行更快,在开发过程中快速获得反馈
- 您的测试与网络条件或数据库状态无关
- 您的测试不会对任何数据存储造成污染,因为它们不会触及数据库
- 对测试所做的任何更改都不会影响后续测试的状态,并且重新运行测试套件应该从一个已知且可重现的起始点开始
- 您不必担心API调用和网络请求的速率限制
当您想要避免副作用(例如写入数据库)或跳过代码中的耗时部分(例如网络访问)时,模拟是有用的,而且还可以避免多次运行测试的影响(例如,想象一个发送电子邮件或调用限速API的函数)。
更重要的是,如果您正在编写一个单元测试,则应该单独测试函数的功能,而不是测试它接触到的所有东西。
使用模拟,您可以检查模块函数是否已被调用以及使用了哪些参数,方法如下:
expect().toHaveBeenCalled()
:检查被追踪函数是否已被调用expect().toHaveBeenCalledTimes()
:计算被追踪函数被调用的次数expect().toHaveBeenCalledWith()
:检查函数是否已使用特定的参数调用expect().toHaveBeenLastCalledWith()
:检查函数最后一次调用的参数
在不影响函数代码的情况下追踪包的调用
当您导入一个包时,可以使用spyOn()
告诉Jest“追踪”特定函数的执行,而不会影响该方法的工作方式。
示例:
1 | const mathjs = require('mathjs') |
模拟整个包
Jest提供了一种方便的方式来模拟整个包。在项目根目录中创建一个__mocks__
文件夹,并在此文件夹中为每个包创建一个JavaScript文件。
假设您导入了mathjs
。在项目根目录中创建一个__mocks__/mathjs.js
文件,并添加以下内容:
1 | module.exports = { |
这将模拟包的log()函数。您可以添加任意数量的要模拟的函数:
1 | const mathjs = require('mathjs') |
模拟单个函数
您可以使用jest.fn()
模拟单个函数:
1 | const mathjs = require('mathjs') |
您还可以使用jest.fn().mockReturnValue('test')
创建一个简单的模拟,除了返回一个值之外,它不执行任何操作。
预先构建的模拟
您可以在常用库中找到预制的模拟。例如,这个包 https://github.com/jefflau/jest-fetch-mock 允许您模拟fetch()
调用,并在测试中提供样本返回值,而无需与实际服务器进行交互。
快照测试
快照测试是Jest提供的一个非常棒的功能。它可以记住您的UI组件是如何呈现的,并将其与当前测试进行比较,如果不匹配,则引发错误。
这是对简单的create-react-app
应用程序的App组件进行的一个简单的测试(确保您安装了react-test-renderer
):
1 | import React from 'react' |
首次运行此测试时,Jest将快照保存在__snapshots__
文件夹中。以下是App.test.js.snap文件的内容:
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP |
如您所见,它是App组件呈现的代码,没有其他内容。
下次运行测试时,测试将输出<App />
的输出并将其与快照进行比较。如果App发生了更改,则会引发错误:
在create-react-app
中使用yarn test
时,您处于监视模式,在那里您可以按w
键显示更多选项:
1 | 监视模式用法 |
如果您的更改是有意的,则按u
键将更新失败的快照,并使测试通过。
您还可以在watch模式之外运行jest -u
(或jest --updateSnapshot
)来更新快照。