Jest测试入门到使用
Jest 测试使用说明
什么是测试
作为开发来讲可以简单把代码分为两种,一种是业务代码,一种是业务无关的代码。测试代码就是典型的业务无关的代码。不过按照我的定义可以把测试代码定位为:可以保证业务代码是否能够按照预期运行的代码。有些人会把这个叫做 TDD,但是TDD(Test-Driven Development or Test-Driven Design)只是开发的一种思路或者说是习惯,先写测试用例,然后实现业务逻辑。
坦白讲,作为实用主义的程序员,先写测试用例还是后写测试用例或是不写测试,只要线上代码运行没问题就行了,测试不测试没那么重要。但是如果是写一个第三方库,业务中台代码,项目核心代码模块。此时的测试代码就十分有必要,如果你做开源如果没有 test
文件夹,可能你的方案就基本没人使用。
为什么做测试
自动化测试就是面向这些问题而生的。
测试分类
单元测试
单元测试是最基础的自动化测试,用来检测项目当中的最小可测单元,例如工具函数、基础组件等
集成测试
在单元测试的基础上,不同功能集成在一起,验证整体功能
E2E测试
相对真实、完整链路的模拟真实操作验证
压力测试
压测的目的是在于找到系统的瓶颈,一定是要确定系统某个方面达到瓶颈了。对于web应用,系统的瓶颈往往会是数据库;系统满负荷运作的时候,数据库的CPU或者是磁盘IO。
ui测试
对ui设计效果的验证,对数据渲染、交互上的验证
测试工具
单元测试(Unit Test)有 Jest, Mocha
UI测试Test Render, Enzyme,
端到端(E2E Test)Cypress.io、Nightwatch.js、Puppeteer、TestCafe
Jest基本使用
1. 安装依赖
# jest 开发依赖npm install --save-dev jest# babel 扩展包npm install --save-dev babel-jest @babel/core @babel/preset-env# Ts测试依赖npm install --save-dev @babel/preset-typescript# jest库 Ts 预处理器npm install --save-dev ts-jest# jest类型定义Typesnpm install --save-dev @types/jest# 另外一个类型定义模块npm install --save-dev @jest/globals
2. 开发支持
我们先写一个两数相加的函数。 首先,创建 src/caculate.js
文件︰
const add = (a, b) => a + bconst remove = (a, b) => a - bconst multiply = (a, b) => a * bconst divide = (a, b) => a / b
module.exports = { add, divide, multiply, remove,}
然后,创建名为 test/sum.test.js
的文件。 此文件中将包含我们的实际测试︰
const { add, divide, multiply, remove } = require('../src/caculate')
describe('calculate module', () => { test('add', () => { expect(add(1, 2)).toBe(3) })
test('remove', () => { expect(remove(1, 2)).toBe(-1) })
test('multiply', () => { expect(multiply(1, 2)).toBe(2) })
test('divide', () => { expect(divide(1, 2)).toBe(0.5) })})
最后,运行 npx test
或 npm run test
,Jest将打印下面这个消息:
PASS test/caculate.test.js
3. 指定配置文件
基于您的项目,Jest将向您提出几个问题,并将创建一个基本的配置文件,每个选项都有一个简短的说明:
jest --init
最后会自动生成这样的配置文件,常用的带有中文说明
module.exports = { // All imported modules in your tests should be mocked automatically // 类型:boolean,默认:false,在每次测试前自动清除模拟的上下文 // automock: false,
// Stop running tests after `n` failures // 失败多少次后停止测试 // bail: 0,
// The directory where Jest should store its cached dependency information // 指定测试缓存文件夹 // cacheDirectory: "C:\\Users\\U\\AppData\\Local\\Temp\\jest",
// Automatically clear mock calls, instances, contexts and results before every test // 在每一个测试之前自动清除模拟类、实例、上下文和结果 clearMocks: true,
// Indicates whether the coverage information should be collected while executing the test // 类型:boolean,默认:false,是否开启 覆盖率 collectCoverage: true,
// An array of glob patterns indicating a set of files for which coverage information should be collected // collectCoverageFrom: undefined,
// The directory where Jest should output its coverage files // 生成的覆盖率文件的文件位置 coverageDirectory: "coverage",
// An array of regexp pattern strings used to skip coverage collection // 需要排除覆盖率文件夹 // coveragePathIgnorePatterns: [ // "\\\\node_modules\\\\" // ],
// Indicates which provider should be used to instrument code for coverage // 覆盖率测试需要的插件 // coverageProvider: "babel",
// A path to a module which exports an async function that is triggered once before all test suites // globalSetup: undefined,
// A path to a module which exports an async function that is triggered once after all test suites // globalTeardown: undefined,
// A set of global variables that need to be available in all test environments // globals: {},
// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. // 最大测试Workers数 // maxWorkers: "50%",
// An array of directory names to be searched recursively up from the requiring module's location // moduleDirectories: [ // "node_modules" // ],
// An array of file extensions your modules use moduleFileExtensions: [ "js", // "mjs", // "cjs", // "jsx", "ts", // "tsx", // "json", // "node" ],
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module // moduleNameMapper: {},
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader // modulePathIgnorePatterns: [],
// Activates notifications for test results // notify: false,
// An enum that specifies notification mode. Requires { notify: true } // notifyMode: "failure-change",
// A preset that is used as a base for Jest's configuration // preset: undefined,
// Run tests from one or more projects // projects: undefined,
// Use this configuration option to add custom reporters to Jest // reporters: undefined,
// Automatically reset mock state before every test // resetMocks: false,
// Reset the module registry before running each individual test // resetModules: false,
// A path to a custom resolver // resolver: undefined,
// Automatically restore mock state and implementation before every test // restoreMocks: false,
// The root directory that Jest should scan for tests and modules within // rootDir: undefined,
// A list of paths to directories that Jest should use to search for files in // roots: [ // "<rootDir>" // ],
// Allows you to use a custom runner instead of Jest's default test runner // runner: "jest-runner",
// The paths to modules that run some code to configure or set up the testing environment before each test // setupFiles: [],
// A list of paths to modules that run some code to configure or set up the testing framework before each test // setupFilesAfterEnv: [],
// The number of seconds after which a test is considered as slow and reported as such in the results. // slowTestThreshold: 5,
// A list of paths to snapshot serializer modules Jest should use for snapshot testing // snapshotSerializers: [],
// The test environment that will be used for testing // 用于模拟测试的测试环境,如果我们用到浏览器的环境(如:document),可以用 jsdom代替 // testEnvironment: "jest-environment-node",
// Options that will be passed to the testEnvironment // 测试环境变量 // testEnvironmentOptions: {},
// Adds a location field to test results // testLocationInResults: false,
// The glob patterns Jest uses to detect test files // 测试匹配规则 testMatch: [ "**/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(spec|test).[tj]s?(x)" ],
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped testPathIgnorePatterns: [ "\\\\node_modules\\\\" ],
// The regexp pattern or array of patterns that Jest uses to detect test files // testRegex: [],
// This option allows the use of a custom results processor // testResultsProcessor: undefined,
// This option allows use of a custom test runner // testRunner: "jest-circus/runner",
// A map from regular expressions to paths to transformers // transform: undefined,
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation // transformIgnorePatterns: [ // "\\\\node_modules\\\\", // "\\.pnp\\.[^\\\\]+$" // ],
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them // unmockedModulePathPatterns: undefined,
// Indicates whether each individual test should be reported during the run // verbose: undefined,
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode // watchPathIgnorePatterns: [],
// Whether to use watchman for file crawling // watchman: true,}
Jest 匹配器
1. 常用匹配器
基本上函数名可以见名知意。
// 精确匹配test('two plus two is four', () => { expect(2 + 2).toBe(4);});test('null', () => { const n = null; expect(n).toBeNull(); expect(n).toBeDefined(); expect(n).not.toBeUndefined(); expect(n).not.toBeTruthy(); expect(n).toBeFalsy();});
// 空值匹配test('zero', () => { const z = 0; expect(z).not.toBeNull(); expect(z).toBeDefined(); expect(z).not.toBeUndefined(); expect(z).not.toBeTruthy(); expect(z).toBeFalsy();});
// 数值匹配test('two plus two', () => { const value = 2 + 2; expect(value).toBeGreaterThan(3); expect(value).toBeGreaterThanOrEqual(3.5); expect(value).toBeLessThan(5); expect(value).toBeLessThanOrEqual(4.5);
// toBe and toEqual are equivalent for numbers expect(value).toBe(4); expect(value).toEqual(4);});
// 字符串匹配test('there is no I in team', () => { expect('team').not.toMatch(/I/);});
test('but there is a "stop" in Christoph', () => { expect('Christoph').toMatch(/stop/);});
// 数组和可迭代对象const shoppingList = [ 'diapers', 'kleenex', 'trash bags', 'paper towels', 'milk',];
test('shoppingList数组中包含milk', () => { expect(shoppingList).toContain('milk'); expect(new Set(shoppingList)).toContain('milk');});
// Exception 匹配function compileAndroidCode() { throw new Error('you are using the wrong JDK!');}
test('compiling android goes as expected', () => { expect(() => compileAndroidCode()).toThrow(); expect(() => compileAndroidCode()).toThrow(Error);
// You can also use a string that must be contained in the error message or a regexp expect(() => compileAndroidCode()).toThrow('you are using the wrong JDK'); expect(() => compileAndroidCode()).toThrow(/JDK/);
// Or you can match an exact error mesage using a regexp like below expect(() => compileAndroidCode()).toThrow(/^you are using the wrong JDK$/); // Test fails expect(() => compileAndroidCode()).toThrow(/^you are using the wrong JDK!$/); // Test pass});
2. 异步代码匹配
为你的测试返回一个Promise,则Jest会等待Promise的resove状态 如果 Promise 的状态变为 rejected, 测试将会失败。
test('the data is peanut butter', () => { return fetchData().then(data => { expect(data).toBe('peanut butter'); });});
// Async/Await 匹配test('the data is peanut butter', async () => { const data = await fetchData(); expect(data).toBe('peanut butter');});
test('the fetch fails with an error', async () => { expect.assertions(1); try { await fetchData(); } catch (e) { expect(e).toMatch('error'); }});
test('the data is peanut butter', async () => { await expect(fetchData()).resolves.toBe('peanut butter');});
test('the fetch fails with an error', async () => { await expect(fetchData()).rejects.toMatch('error');});
Jest中级使用
全局函数
我们通过 sum.test.js
文件,发现了 describe
和 expect
,但我们并没有引入对应的函数,却能正常的使用,这是为什么呢?
实际上 Jest
会将这些方法和对象注入到测试文件的 全局环境
里,所以我们在使用的时候并不需要通过 import
或 require
当然,如果你一定要引用,可以这样引用:
import {describe, expect, test} from '@jest/globals
describe
describe: 描述块,将一组功能相关的测试用例组合在一块
it
it: 别名 test
, 用来存放测试用例,每一个it
就是一个测试用例
钩子函数
写测试的时候你经常需要在运行测试前做一些准备工作,和在运行测试后进行一些整理工作。 Jest 提供辅助函数来处理这个问题。
afterAll 和 beforeAll
afterAll: 所有的测试用例执行完 后
执行的方法,如果传入的回调函数返回值是 promise
或者 generator
, Jest
会等待 promise resolve
再继续执行。
beforeAll: 与 afterAll
相反, 所有的测试用例执行之 前
执行的方法
afterEach 和 beforeEach
afterEach: 也 afterAll
相比, afterEach
可以在每个测试完成 后
都运行一遍
beforeEach: beforeEach
可以在每个测试完成之 前
都运行一遍
// global.js
export const global = function(msg) { console.log(msg)}
// global.test.jsimport { add } from '../src/caculate'import { global } from '../src/global'
beforeAll(() => { console.log('beforeAll')})
afterAll(() => { console.log('afterAll')})
describe('calculate module', () => {
test('global start', () => { expect(1).toBe(1) global('global start') })
test('global end', () => { expect(1).toBe(1) global('global end') })
})
运行结果
加上 beforeEach
和 afterEach
import { global } from '../src/global'
beforeAll(() => { console.log('beforeAll')})
afterAll(() => { console.log('afterAll')})
beforeEach(() => { console.log('beforeEach')})
afterEach(() => { console.log('afterEach')})
describe('calculate module', () => {
test('global start', () => { expect(1).toBe(1) global('global start') })
test('global end', () => { expect(1).toBe(1) global('global end') })
})
运行结果,可以看到每一个 test
之前都运行了beforeEach
运行之后立马云 afterEach
Jest高级使用
模拟函数
Mock 函数允许你测试代码之间的连接——实现方式包括:擦除函数的实际实现、捕获对函数的调用 ( 以及在这些调用中传递的参数) 、在使用 new
实例化时捕获构造函数的实例、允许测试时配置返回值。
有两种方法可以模拟函数:要么在测试代码中创建一个 mock 函数,要么编写一个手动 mock
来覆盖模块依赖。
假设我们要测试函数 forEach
的内部实现,这个函数为传入的数组中的每个元素调用一次回调函数。
function forEach(items, callback) { for (let index = 0; index < items.length; index++) { callback(items[index]); }}
为了测试此函数,我们可以使用一个 mock 函数,然后检查 mock 函数的状态来确保回调函数如期调用。
const mockCallback = jest.fn(x => 42 + x);forEach([0, 1], mockCallback);
// 此 mock 函数被调用了两次expect(mockCallback.mock.calls.length).toBe(2);
// 第一次调用函数时的第一个参数是 0expect(mockCallback.mock.calls[0][0]).toBe(0);
// 第二次调用函数时的第一个参数是 1expect(mockCallback.mock.calls[1][0]).toBe(1);
// 第一次函数调用的返回值是 42expect(mockCallback.mock.results[0].value).toBe(42);
覆盖率报告
// jest.config.js collectCoverage: true,coverageDirectory: "coverage",
在 jest.config.js
中添加如下两个选项。运行测试
%stmts:是 语句覆盖率(statement coverage)
,是不是每个语句都执行了
%Branch:是 分支覆盖率(branch coverage)
,是不是每个if代码块都执行了
%Funcs:是 函数覆盖率(functioncoverage
,是不是每个函数都调用了
%Lines:是 行覆盖率(line coverage
,是不是每一行都执行了
查看 coverage
文件夹
打开index.html
查看结果
最后可以使用Circle
等集成工具自动化测试
项目代码地址
https://github.com/web0matrix/Web0matrix/tree/main/jest 欢迎点赞。模拟函数部分使用较少,还有待补充。
参考
https://mp.weixin.qq.com/s/2mcazw7-nL-0j1-0qkvsKQ
https://jestjs.io/zh-Hans/docs/setup-teardown
本文由 mdnice 多平台发布
Jest测试入门到使用相关推荐
- 使用Jest测试JavaScript (入门篇)
1 什么是 Jest? Jest是 Facebook 的一套开源的 JavaScript 测试框架, 它自动集成了断言.JSDom.覆盖率报告等开发者所需要的所有测试工具,是一款几乎零配置的测试框架. ...
- 使用Jest测试JavaScript (入门篇) 1
1 什么是 Jest? Jest Jest是 Facebook 的一套开源的 JavaScript 测试框架, 它自动集成了断言.JSDom.覆盖率报告等开发者所需要的所有测试工具,是一款几乎零配置的 ...
- Puppeteer E2E测试入门
Puppeteer E2E测试入门 本文内容涉及ES6 async.jest的相关知识,对于以上内容不太了解的读者可以先了解相关内容. Puppeteer是什么 它由Chrome官方团队提供,通过De ...
- Jest测试框架学习(一)
前言 jest是针对JavaScript的测试框架.如果遵循TDD原则,在任何功能开发之前都需要先写测试.而测试分为单元测试,集成测试和系统测试. 单元测试 单元测试可以理解为对于单个函数(单一功能) ...
- Android渗透测试Android渗透测试入门教程大学霸
Android渗透测试Android渗透测试入门教程大学霸 第1章 Android渗透测试 Android是一种基于Linux的自由及开放源代码的操作系统,主要用于移动设备,如智能手机.平板等.目前 ...
- jest测试ajax,ajax – 如何使用Jest来测试React呈现的异步数据?
我正在使用React for render和Jest / Jasmine进行测试.我使用旧的Jest / Jasmine等待测试并运行,但现在这些已经在Jasmine 2中消失了,我不知道如何用新的a ...
- react jest测试_如何使用React测试库和Jest开始测试React应用
react jest测试 Testing is often seen as a tedious process. It's extra code you have to write, and in s ...
- react jest测试_如何使用Jest和react-testing-library测试Socket.io-client应用程序
react jest测试 by Justice Mba 由Mba法官 如何使用Jest和react-testing-library测试Socket.io-client应用程序 (How to test ...
- react jest测试_如何设置Jest和Enzyme来测试React Native应用
react jest测试 by Sam Ollason 通过萨姆·奥拉森(Sam Ollason) This short article shares my experiences setting u ...
最新文章
- 广东计算机一级考试可以用计算器吗,你考试用不用计算器?
- 小胖机器人能刷碗吗_小胖机器人好不好?透过真相看本质
- python如何判断是否有弹出框_Selenium2+python自动化47-判断弹出框存在(alert_is_present)【转载】...
- SpringBoot2.0 Actuator 监控参数说明
- [签名算法]DSA 算法
- 2048游戏c语言linux简易代码,C语言实现2048游戏代码
- iview table数据直接导出_使用iview的exportcsv怎样导出嵌套数据
- 这几个公众号带你看看BAT的工作情况
- 《Java与模式》笔记(一)
- PostgreSQL 优势,MySQL 数据库自身的特性并不十分丰富,触发器和存储过程的支持较弱,Greenplum、AWS 的 Redshift 等都是基于 PostgreSQL 开发的...
- 新硬盘显示有储存空间但无法分区_容量更足高速无忧,奥睿科迅龙V500 NVME硬盘体验...
- 数学算法对计算机编程的优化
- cad图形不见了怎么办_CAD软件常见问题解答,CAD中的工具栏不见了怎么办?如何清理图形...
- Unity Remote5 使用
- 基于云服务创建实时运营数据分析服务(二)
- 苹果手机iCloud备忘录删除怎么恢复
- Python切片工具 pillow
- 生而为人,我很抱歉!深夜爬虫, 我很抱歉 ,附微信 “ 网抑云” 公众号爬虫教程!
- jsp+servlet实现个人博客系统
- 计算机毕业设计ssm陈氏商城9pd36系统+程序+源码+lw+远程部署