凡科建设网站怎么保存,广州网站的建设,互联网络推广,抖音关键词seo系统Jest
Jest 概述
Jest是一个领先的JavaScript测试框架#xff0c;特别适用于React和Node.js环境。由Facebook开发#xff0c;它以简单的配置、高效的性能和易用性而闻名。Jest支持多种类型的测试#xff0c;包括单元测试、集成测试和快照测试#xff0c;后者用于捕获组件或…Jest
Jest 概述
Jest是一个领先的JavaScript测试框架特别适用于React和Node.js环境。由Facebook开发它以简单的配置、高效的性能和易用性而闻名。Jest支持多种类型的测试包括单元测试、集成测试和快照测试后者用于捕获组件或数据结构的状态以便于后续的比较和验证。Jest自动化模拟依赖项和异步代码测试提高了测试的可靠性和灵活性。其并行测试执行机制显著加快了测试过程而交互式监视模式则在开发过程中提供即时反馈。此外Jest还提供内置的代码覆盖率工具帮助开发者优化测试范围。因其强大的功能和广泛的社区支持Jest成为现代JavaScript项目中不可或缺的测试工具。
Jest 环境配置
安装包
1、jest这是 Jest 测试框架本身。
2、types/jest这是 Jest 的 TypeScript 类型定义用于在使用 TypeScript 编写测试时提供类型检查和自动完成功能。
3、babel-jest这是用于将 Jest 集成到使用 Babel 的项目中的插件。它允许 Jest 处理通过 Babel 转换的代码。
4、ts-jest这是一个 Jest 转换器用于处理 TypeScript 文件。它基本上允许 Jest 理解和运行 TypeScript 测试代码。
5、jest-transform-stub这个插件用于处理非 JavaScript 资源如 CSS 和图片的导入这在 Jest 测试中通常会被忽略或需要特殊处理。
npm install --save-dev jest types/jest babel-jest ts-jest jest-transform-stub6、testing-library/jest-dom提供一套针对 DOM 元素的 Jest 断言非常适用于在测试 React 组件时使用。
7、testing-library/react用于测试 React 组件它提供了渲染组件、查询 DOM 元素以及与组件交互的工具。
8、testing-library/user-event这个库用于模拟用户事件如点击、输入等可用于更逼真地测试用户交互。
npm install --save-dev testing-library/jest-dom testing-library/react testing-library/user-event9、eslint-plugin-jest这是一个 ESLint 插件提供针对 Jest 测试的特定规则有助于保持测试代码的质量和一致性。
10、react-test-renderer: 这是一个用于渲染 React 组件为 JavaScript 对象的库常用于 Jest 的快照测试。它可以在不需要 DOM 环境的情况下测试 React 组件的输出这对于在 Node 环境下运行的 Jest 测试非常有用。
npm install --save-dev eslint-plugin-jest react-test-rendererpackage.json
1、基本的运行测试用例配置npm test即可运行
--watchAll这个参数告诉 Jest 进入 “watch” 模式。在这个模式下 Jest 会监视项目中的文件变化。当修改并保存了代码文件包括测试文件和被测试的源代码文件时 Jest 会自动重新运行相关的测试。 --watchAll 与 --watch 不同之处在于--watchAll 会在初次运行时执行所有测试而 --watch 只在检测到文件更改时运行相关测试。 test: jest --watchAll,2、运行某个文件夹下的所有测试文件src/tests代表文件夹路径
test:folder: jest --watchAll --testPathPatternsrc/tests,3、单独运行某个测试文件src/renderer/login/loginApi.test.tsx代表需要测试的文件路径
test:single: jest --watchAll jest --findRelatedTests src/renderer/login/loginApi.test.tsx,常见的 Jest 命令行操作
1、f 只会跑测试未通过的用例再次点击 f 会取消当前模式。
2、o 只监听已改变的文件如果存在多个测试文件可以开启会与当前 git 仓库中的提交进行比较需要使用 git 来监听哪个文件修改了也可以将 --watchAll 改为 --watch 只会运行修改的文件。
3、a 运行所有测试如果在 watch 模式中使用了 f 或 o 使用 a 可以恢复运行所有测试。
4、u 用于更新 Jest 快照测试中的快照。如果更改了渲染组件的输出可以使用此命令更新快照。
5、w 显示 Jest watch 模式中的所有可用命令和选项的列表。
6、q 退出 Jest 的 watch 模式。
7、i 只会运行之前运行失败的测试文件但提供更交互式的体验。
.babelrc
当使用 Jest 测试一个使用 Babel 编译的项目时Jest 会通过这些配置来正确处理和理解 JavaScript 代码。
{// 设置插件集合presets: [// 使用当前插件可以进行转换// 数组的第二项为插件的配置项[babel/preset-env,{// 根据 node 的版本号来结合插件对代码进行转换targets: {node: current}}]]
}setupTests.js
该文件设置测试环境中的全局对象和模拟mock某些模块在本项目中针对 Electron 和 Node.js 的相关模块进行模拟以便在不依赖实际 Electron 或浏览器环境的情况下测试特定的功能。
/* eslint-disable no-undef */
const electronMock require(./__Mock__/electronMock)global.window.require jest.fn(moduleName {if (moduleName electron) {return electronMock}if (moduleName electron/remote) {return {require: jest.fn(module {// 模拟 Node.js 模块如 fsif (module fs) {return {} // 返回 fs 的模拟实现}// 其他模块模拟...}),}}
})
global.window.matchMedia global.window.matchMedia ||function () {return {matches: false,addListener: function () {},removeListener: function () {},}}jest.config.ts
jest.config.ts 是一个使用 TypeScript 编写的 Jest 配置文件。可以使用npx jest --init初始化命令来生成一个基本的配置文件。
export default {// 自动清除 mock 调用和实例clearMocks: true,// 开启代码覆盖率收集collectCoverage: true,// 代码测试覆盖率通过分析那些文件生成的!代表不要分析collectCoverageFrom: [**/*.{ts,js,tsx}, !**/node_modules/**, !**/vendor/**],// 代码覆盖率报告的输出目录coverageDirectory: coverage,// 代码覆盖率的收集器这里使用 V8 引擎coverageProvider: v8,// 代码覆盖率报告的格式coverageReporters: [text-summary,lcov,],globals: {ts-jest: {// 关闭 ts-jest 的诊断信息diagnostics: false,},},// 引入模块时进行自动查找模块类型逐个匹配moduleFileExtensions: [js, jsx, ts, tsx, json, node],// 模块名字使用哪种工具进行映射moduleNameMapper: {^/(.*)$: rootDir/src/$1, //将 / 映射到 src/ 目录\\.(css|less)$: jest-transform-stub,^localTypes$: rootDir/src/types.ts,^localUtils$: rootDir/src/utils/index.ts,^localConst$: rootDir/src/utils/constants.ts,^Assets/(.*)$: rootDir/assets/$1,},preset: ts-jest,rootDir: undefined,// 检测从哪个目录开始rootDir 代表根目录roots: [rootDir/src],// 在运行测试之前执行的文件设置测试环境setupFilesAfterEnv: [./setupTests.js],// 测试运行的环境会模拟 domtestEnvironment: jsdom,// 哪些文件会被认为测试文件testMatch: [// src 下的所有 __tests__ 文件夹中的所有的 js jsx ts tsx 后缀的文件都会被认为是测试文件rootDir/src/**/__tests__/**/*.{js,jsx,ts,tsx},// scr 下的所有以 .test/spec.js/jsx/ts/tsx 后缀的文件都会被认为是测试文件rootDir/src/**/*.{spec,test}.{js,jsx,ts,tsx},],// 测试时忽略的路径testPathIgnorePatterns: [\\\\node_modules\\\\],// 测试文件中引用一下后缀结尾的文件会使用对应的处理方式transform: {^.\\.(t|j)s$: ts-jest,\\.svg$: rootDir/__Mock__/svgTransform.js,},
}__Mock__文件夹
文件夹用于存放模拟mock模块
自动模拟当调用jest.mock(moduleName)时Jest会查找__mocks__文件夹中名为 moduleName 的文件并自动使用该文件中的模拟实现。这意味着不需要在每个测试文件中手动设置模拟。
第三方模块模拟这个机制不仅适用于自定义模块也适用于第三方模块。例如正在使用一个发送 fetch 请求的库可以在 __mocks__文件夹中创建一个模拟以避免在测试中发出真实的网络请求。
Mock
Mock fetch 或其他 HTTP 请求库的调用
待补充
Mock 函数
jest.fn()Mock 第三方模块
待补充
全局函数 describe 和 it
describe 用于将测试分组而 it 用于定义单个具体的测试用例。可以在 describe 块中放置多个 it 测试用例也可以嵌套其他 describe 块以创建更详细的测试结构。
// 用于创建一个测试套件将一组功能或逻辑相关的测试用例组织在一起
describe(测试输入框的校验规则, () {// it 的第一个参数是一个字符串描述了测试用例应该做什么有助于代码的可读性和测试结果的理解it(输入正常, async () {// ...});it(必填, async () {// ...});it(仅支持汉字、字母、数字和-_%., async () {// ...})it(以数字、字母或汉字开头, async () {// ...})it(限长, async () {// ...})
});断言 expect
用于验证代码的行为是否符合预期。 expect 函数接受一个参数———想要测试的值。然后expect 返回一个“期望对象”这个对象提供了一系列“匹配器”matcher方法用于声明对这个值的期望。
describe(测试输入框的校验规则, () {it(必填, async () {// ...expect(message).toBeInTheDocument()})it(仅支持汉字、字母、数字和-_%., async () {// ...expect(message).toBeInTheDocument()})it(以数字、字母或汉字开头, async () {// ...expect(message).toBeInTheDocument()})it(限长, async () {// ...expect(message).toBeInTheDocument()})it(输入正常, async () {// ...await waitFor(() {expect(input.className).toMatch(ant-input-status-success)})})
})匹配器
toBe 期待是否与匹配器中的值相等相当于object.is
toMatch 匹配当前字符串中是否含有这个值支持正则
toContain 用于检查数组或字符串是否包含特定项或子串
toBeInTheDocument 判断某个元素是否在文档中即是否已被渲染到 DOM 上
toHaveProperty 用于检查对象是否具有特定属性可以选择性地检查属性值
toEqual 是“相等”不是“相同”相当于
toBeFalsy 和 toBeTruthy 检查一个值是否为假或真
toBeNull 专门用来检查一个值是否为 null
toBeDefined 和 toBeUndefined 这些断言用于检查变量是否已定义或未定义
toThrow 用于检查函数是否抛出错误
not用于对断言取反
snapshot 快照
会在当前测试文件位于的文件夹下生成一个__snapshots__文件夹该文件夹下会生成扩展名为 .snap 文件文件会保存代码运行的结果如渲染的组件树、数据结构等。
toMatchSnapshot 方法接受一个参数是快照名称字符串类型。
expect(container).toMatchSnapshot(必填)一定要是 container 不能是 screen 用 screen 不会保存 DOM 结构 优势 自动化比较Jest 自动比较快照减少了手动检查输出的需要。
简化复杂结构的测试对于复杂的对象或大型UI组件编写传统测试断言可能很困难。快照测试可以轻松捕获整个结构。
文档化变化快照文件也可以作为代码行为的一种文档让开发者和审阅者理解代码更改的影响。
快照更新当代码发生更改导致快照不再匹配时可以使用 jest --updateSnapshot 命令或jest -u命令来更新快照。
测试用例覆盖率报告
会在主文件夹下生成一个名为 coverage 的文件夹打开里面的 html 就可以看到各个文件的覆盖率通常包含以下几种主要的覆盖率类型
行覆盖率Line Coverage测量有多少行代码被测试用例执行过。如果一行代码在测试中至少被执行一次那么这一行就被认为是覆盖了的。
函数覆盖率Function Coverage测量有多少个函数或方法被测试用例调用过。即使函数内的某些行没有被执行只要函数被调用它就被认为是覆盖了的。
分支覆盖率Branch Coverage测量代码中的每个if语句、循环、switch语句等的每个分支是否都被执行过。这是检查条件语句的完整性的重要指标。
语句覆盖率Statement Coverage测量有多少个独立语句被测试执行过。这与行覆盖率类似但关注的是语句的执行。
React Testing Library
render
渲染 React 组件到一个虚拟的 DOM 环境中以便进行测试。
render 函数接受一个 React 组件作为参数并返回一个包含多个属性和方法的对象例如 container 和 debug 。 container 可以调用各类查询函数在渲染的组件中查找元素 debug 可以打印出 baseElement 的内部HTML用于调试。
describe(测试输入框的校验规则, () {it(输入正常, async () {const Com Index /const container render(Com)container.debug()})
})screen
在使用 React Testing Library 进行测试时通常会先用 render 函数渲染组件然后用 screen 查询和操作元素。screen 对象可以在测试文件中全局访问无需在每个测试中单独导入或创建。
describe(测试输入框的校验规则, () {it(输入正常, async () {render(Index /)screen.debug()})
})查询函数
React Testing Library 提供了一系列的查询函数用于在 Jest 测试中找到 DOM 节点。
getBy…
getByText: 根据文本内容查找元素。
getByLabelText: 根据关联的 label 文本查找 input, select, 或 textarea 元素。
getByPlaceholderText: 根据占位符文本查找输入框。
getByAltText: 根据图片的 alt 属性文本查找图片元素。
getByTitle: 根据 title 属性查找元素。
getByRole: 根据 ARIA 角色查找元素。
getByTestId: 根据 data-testid 属性查找元素。
queryBy…
queryBy...函数的行为类似于 getBy... 函数但当查询的元素不存在时它们返回 null 而不是抛出错误。这对于断言某个元素不在页面上非常有用。
findBy…
findBy...函数是 getBy... 函数的异步版本。它们返回一个 Promise适用于等待异步操作完成后元素出现在 DOM 中的情况。
…AllBy…, queryAllBy…, findAllBy…
这些函数的行为类似于 getBy..., queryBy..., 和 findBy...但用于返回多个匹配的元素。如果没有找到匹配的元素getAllBy... 和 findAllBy... 会抛出错误而 queryAllBy... 返回一个空数组。
总结
getBy... 函数用于当确定元素存在时。如果元素不存在测试将失败。 queryBy... 函数用于当元素可能不存在需要处理这种情况时。 findBy... 函数用于处理异步逻辑当需要等待元素出现时。 ...AllBy... 函数用于处理有多个匹配元素的情况。
// findByText参数必须是完整的文本如果是子字符串需要加上{exact: false}
// findByText不管前缀是screen还是container都可以成功
describe(测试输入框的校验规则, () {it(仅支持汉字、字母、数字和-_%., async () {const Com Index /const container render(Com)const input await screen.findByRole(textbox)await userEvent.type(input, )const messages await container.findByText(溶剂名称仅支持汉字、字母、数字和-_%.)})
})
describe(测试输入框的校验规则, () {it(仅支持汉字、字母、数字和-_%., async () {const Com Index /const container render(Com)const input await screen.findByRole(textbox)await userEvent.type(input, )const messages await screen.findByText(仅支持汉字、字母、数字和-_%., {exact: false})})
})waitFor
用于处理异步操作和元素的异步更新。waitFor 常与异步查询函数如 findBy…结合使用用于处理组件状态更新或数据加载。
describe(测试输入框的校验规则, () {it(输入正常, async () {const container render(Index /)screen.debug()const input await screen.findByRole(textbox)await waitFor(() {expect(screen.getByText(必填, { exact: false })).toBeInTheDocument()})})
})fireEvent 和 userEvent
Jest 提供fireEvent和userEvent模拟用户操作。
fireEvent直接同步触发 DOM 事件。当调用 fireEvent 的任何方法时如 fireEvent.click它会立即生成对应的 DOM 事件并同步地传递给目标元素。因此fireEvent 方法调用后不会返回 Promise也不涉及任何异步操作所以通常不需要使用 await 关键字。
userEvent旨在更贴近用户的实际操作因此它经常涉及到一系列复杂的、可能是异步的事件。例如当用户在输入框中输入文字时这不仅仅是一个简单的同步操作。它包含了一系列的键盘和输入事件这些事件可能会触发各种事件处理器这些处理器本身可能是异步的。
1、fireEvent来自testing-library/reactuserEvent来自testing-library/user-event
2、fireEvent的清空 Input 输入框操作为fireEvent.change(input, {target: {value: }})userEvent的清空 Input 输入框操作为userEvent.type(input, {backspace})
3、fireEvent前不需要添加awaituserEvent需要。
总结如果需要模拟简单的事件并需要完全控制这些事件的属性fireEvent 是个好选择。而如果需要模拟更复杂或更接近真实用户行为的交互userEvent 则更合适。
describe(测试输入框的校验规则, () {it(仅支持汉字、字母、数字和-_%., async () {const Com Index /const container render(Com)const input await screen.findByRole(textbox)fireEvent.change(input, {target: {value: }})})
})
describe(测试输入框的校验规则, () {it(仅支持汉字、字母、数字和-_%., async () {const Com Index /const container render(Com)const input await screen.findByRole(textbox)await userEvent.type(input, )})
})Jest 测试案例
测试 Input 输入框的校验规则
当前 Input 输入框的校验规则
1必填
2限长100
3仅支持汉字、字母、数字和-_%.
4必须以数字、字母或汉字开头
const nameRules ({label,max 10,required true,
}: {label: stringmax?: numberrequired?: boolean
}): Rule[] [{ required, message: ${label}必填 },{ type: string, max, message: ${label}限长${max} },{pattern: /^([a-zA-Z0-9\u4E00-\u9FA5_.%-])*$/g,message: ${label}仅支持汉字、字母、数字和-_%.,},{pattern: /^([0-9|a-zA-Z0-9|\u4E00-\u9FA5])/g,message: ${label}以数字、字母或汉字开头,},]因被测试组件的复杂程度不同测试同一个功能所用的 API 也不同
1被测试功能组件的简单版
该组件只有基本的页面布局和nameRules校验规则
/* eslint-disable react-hooks/rules-of-hooks */
import { nameRules } from /utils/constants
import { Form, Input } from antdconst myInput () {return (FormForm.ItemlabelUsernamenameusername// 校验规则rules{nameRules({label: 名称,required: true,})}Input //Form.Item/Form)
}
export default myInput在测试较简单的组件时模拟用户操作可以使用fireEvent.change()断言也无需包裹在waitFor中便可同步执行。
/* eslint-disable no-undef */
import { fireEvent, render, screen, waitFor } from testing-library/react
import Index from ./index
import testing-library/jest-domdescribe(测试输入框的校验规则, () {it(必填, async () {// 渲染组件const Com Index /const container render(Com)// findByRole不管前缀是screen还是container都可以成功const input await screen.findByRole(textbox)// 在 input 输入框中输入1fireEvent.change(input, { target: { value: 1 } })// 清空 inputfireEvent.change(input, { target: { value: } })// findByText参数必须是完整的文本如果是子字符串需要加上{exact: false}expect(await container.findByText(必填, { exact: false })).toBeInTheDocument()})it(仅支持汉字、字母、数字和-_%., async () {const Com Index /const container render(Com)const input await screen.findByRole(textbox)fireEvent.change(input, { target: { value: } })expect(await container.findByText(仅支持汉字、字母、数字和-_%., { exact: false })).toBeInTheDocument()})it(以数字、字母或汉字开头, async () {const Com Index /const container render(Com)const input await screen.findByRole(textbox)fireEvent.change(input, { target: { value: - } })expect(await container.findByText(以数字、字母或汉字开头, { exact: false })).toBeInTheDocument()})it(限长, async () {const Com Index /const container render(Com)const input await screen.findByRole(textbox)fireEvent.change(input, { target: { value: a.repeat(101) } })expect(await container.findByText(限长, { exact: false })).toBeInTheDocument()})it(输入正常, async () {const Com Index /const container render(Com)const input await screen.findByRole(textbox)fireEvent.change(input, { target: { value: 1 } })await waitFor(() {expect(input.className).toMatch(ant-input-status-success)})})
})2被测试功能组件的复杂版
该组件是个集合组件功能比较复杂被测试的输入框只是其中一小部分内容。
因为组件存在 fetch 接口的请求但是 jest 测试不会运行真实的 fetch 接口所以需要 mock 数据在本组件中通过在catch中给定初始数据。
在复杂环境下render组件时需要 mock 渲染组件所需的各项参数在本组件中id值是直接给定一个存在的 id onCancel方法 mock 一个空函数Dn初始化数据。
此时模拟用户操作须使用await userEvent.type()断言外须包裹await waitFor(() {})。
/* eslint-disable no-undef */
import { fireEvent, render, screen, waitFor } from testing-library/react
import ComplexIndex from ./ComplexIndex
import testing-library/jest-dom
import userEvent from testing-library/user-eventdescribe(测试输入框的校验规则, () {const onCancelMock jest.fn()it(必填, async () {// 渲染组件render(ComplexIndexid93eonCancel{onCancelMock}Dn{{dn1: 1,dn2: ,}}/)const input await screen.findByRole(textbox)// 在 input 输入框中输入“正常输入”await userEvent.type(input, 1)// 清空 inputawait userEvent.type(input, {backspace})// 异步等待断言执行await waitFor(() {expect(screen.getByText(必填, { exact: false })).toBeInTheDocument()})})it(正常输入, async () {render(ComplexIndexid93eonCancel{onCancelMock}Dn{{dn1: 1,dn2: ,}}/)const input await screen.findByRole(textbox)await userEvent.type(input, 正常输入)await waitFor(() {expect(input.className).toMatch(ant-input-status-success)})})it(限长, async () {render(ComplexIndexid93eonCancel{onCancelMock}Dn{{dn1: 1,dn2: ,}}/)const input await screen.findByRole(textbox)await userEvent.type(input, a.repeat(101))await waitFor(() {expect(screen.getByText(限长, { exact: false })).toBeInTheDocument()})})it(仅支持汉字、字母、数字和-_%., async () {render(ComplexIndexid93eonCancel{onCancelMock}Dn{{dn1: 1,dn2: ,}}/)const input await screen.findByRole(textbox)await userEvent.type(input, )await waitFor(() {expect(screen.getByText(仅支持汉字、字母、数字和-_%., { exact: false })).toBeInTheDocument()})})it(以数字、字母或汉字开头, async () {render(ComplexIndexid93eonCancel{onCancelMock}Dn{{dn1: 1,dn2: ,}}/)const input await screen.findByRole(textbox)await userEvent.type(input, -)await waitFor(() {expect(screen.getByText(以数字、字母或汉字开头, { exact: false })).toBeInTheDocument()})})
})3获取原始 DOM 内容进行测试
Input 标签有aria-describedby属性该属性的属性值是某个div的id该div下的div包含所有类型的报错字样。
/* eslint-disable no-undef */
import { fireEvent, render, screen } from testing-library/react
import Index from ./index
import testing-library/jest-dom
import userEvent from testing-library/user-eventdescribe(测试输入框的校验规则, () {it(仅支持汉字、字母、数字和-_%., async () {// 渲染被测组件const Com Index /const container render(Com)// 获取input元素const input await screen.findByRole(textbox)// 在input输入框中输入await userEvent.type(input, )// 获取input元素const inputEl document.querySelector(input[typetext])// 获取input元素的所有属性const attributes inputEl!.attributeslet ariaDescribedby for (let i 0; i attributes?.length; i) {console.log(attributes[i].name, attributes[i].value)// 找到aria-describedby属性if (attributes[i].name aria-describedby) {// 获取 aria-describedby 属性的值ariaDescribedby attributes[i].value}}// div 的 id 值为 aria-describedby 属性的值const borderDiv document.getElementById(ariaDescribedby)const childrenDiv borderDiv?.querySelectorAll(div)childrenDiv?.forEach(div {// 报错文本console.log(div.textContent)})})
})Jest VSCode 插件
1、Jest
内置测试运行器可以直接运行和调试 jest 测试可以测试某个 describe 或者某个 describe 中的单个 it