(2.2w字)前端单元测试之Jest详解篇

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-stub

6、@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-event

9、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-renderer

package.json

1、基本的运行测试用例配置,npm test即可运行

--watchAll:这个参数告诉 Jest 进入 “watch” 模式。在这个模式下, Jest 会监视项目中的文件变化。当修改并保存了代码文件(包括测试文件和被测试的源代码文件)时, Jest 会自动重新运行相关的测试。

--watchAll--watch 不同之处在于,--watchAll 会在初次运行时执行所有测试,而 --watch 只在检测到文件更改时运行相关测试。

"test": "jest --watchAll",

2、运行某个文件夹下的所有测试文件,src/tests代表文件夹路径

"test:folder": "jest --watchAll --testPathPattern=src/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 模块,如 fs
				if (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'],
    // 测试运行的环境,会模拟 dom
    testEnvironment: '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 提供fireEventuserEvent模拟用户操作。

fireEvent:直接同步触发 DOM 事件。当调用 fireEvent 的任何方法时(如 fireEvent.click),它会立即生成对应的 DOM 事件,并同步地传递给目标元素。因此,fireEvent 方法调用后不会返回 Promise,也不涉及任何异步操作,所以通常不需要使用 await 关键字。

userEvent:旨在更贴近用户的实际操作,因此它经常涉及到一系列复杂的、可能是异步的事件。例如,当用户在输入框中输入文字时,这不仅仅是一个简单的同步操作。它包含了一系列的键盘和输入事件,这些事件可能会触发各种事件处理器,这些处理器本身可能是异步的。

1、fireEvent来自'@testing-library/react'userEvent来自@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: string
    max?: number
    required?: 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 'antd'

const myInput = () => {
    return (
        <Form>
            <Form.Item
                label="Username"
                name="username"
                // 校验规则
                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-dom'

describe('测试输入框的校验规则', () => {
    it('必填', async () => {
        // 渲染组件
        const Com = <Index />
        const container = render(Com)
        // findByRole不管前缀是screen还是container都可以成功
        const input = await screen.findByRole('textbox')
        // 在 input 输入框中输入1
        fireEvent.change(input, { target: { value: '1' } })
        // 清空 input
        fireEvent.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-event'

describe('测试输入框的校验规则', () => {
    const onCancelMock = jest.fn()

    it('必填', async () => {
        // 渲染组件
        render(
            <ComplexIndex
                id="93e"
                onCancel={onCancelMock}
                Dn={{
                    dn1: 1,
                    dn2: '',
                }}
            />
        )
        const input = await screen.findByRole('textbox')
        // 在 input 输入框中输入“正常输入”
        await userEvent.type(input, '1')
        // 清空 input
        await userEvent.type(input, '{backspace}')
        // 异步等待断言执行
        await waitFor(() => {
            expect(screen.getByText('必填', { exact: false })).toBeInTheDocument()
        })
    })
    it('正常输入', async () => {
        render(
            <ComplexIndex
                id="93e"
                onCancel={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(
            <ComplexIndex
                id="93e"
                onCancel={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(
            <ComplexIndex
                id="93e"
                onCancel={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(
            <ComplexIndex
                id="93e"
                onCancel={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属性,该属性的属性值是某个divid,该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-event'

describe('测试输入框的校验规则', () => {
    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[type='text']")
        // 获取input元素的所有属性
        const attributes = inputEl!.attributes
        let 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/391119.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

离线数仓(二)【用户行为日志采集平台搭建】

用户行为日志采集平台搭建 1、用户行为日志概述 用户行为日志的内容&#xff0c;主要包括用户的各项行为信息以及行为所处的环境信息。收集这些信息的主要目的是优化产品和为各项分析统计指标提供数据支撑。收集这些信息的手段通常为埋点。 目前主流的埋点方式&#xff0c;有代…

C++文件操作->文本文件(->写文件、读文件)、二进制文件(->写文件、读文件)

#include<iostream> using namespace std; #include <fstream>//头文件包含 //文本文件 写文件 void test01() { //1.包含头文件 fstream //2.创建流对象 ofstream ofs; //3.指定打开方式 ofs.open("test.txt", ios::out); //4.写…

【杂谈】裁我?我是研发,我是研发啊!

闲谈 这两年互联网是越来越不太平了&#xff0c;前有国外互联网裁员的妖风四起&#xff0c;后来寒气又传到国内&#xff0c;让我们这群打工人叫苦连天。最近有部电影蛮火的&#xff0c;叫《年会不能停》&#xff0c;感觉跟我前司很相似&#xff0c;不过好像由于今年业绩不太行…

第1集《佛遗教经》

《佛遗教经》和尚尼慈悲&#xff0c;诸位法师、诸位居士&#xff0c;阿弥陀佛&#xff01;好&#xff0c;请放掌。 我们从今天开始有六个讲次&#xff0c;跟大家共同学习《佛遗教经》。在正式讲这部经之前&#xff0c;我想先简单的说明本经的特色。 身为一个佛弟子&#xff0…

OpenCV-40 绘制直方图

一、使用matplotlib画直方图 可以利用matplotlib把OpenCV统计得到的直方图绘制出来 示例代码如下&#xff1a; import cv2 import matplotlib.pyplot as pltlena cv2.imread("beautiful women.png") # 变为黑白图片 gray cv2.cvtColor(lena, cv2.COLOR_BGR2GRAY…

《Linux 简易速速上手小册》第8章: 安全性与加固(2024 最新版)

文章目录 8.1 防火墙与安全策略8.1.1 重点基础知识8.1.2 重点案例&#xff1a;配置 iptables 以保护 Web 服务器8.1.3 拓展案例 1&#xff1a;使用 firewalld 配置动态防御区域8.1.4 拓展案例 2&#xff1a;配置 ufw 以简化管理 8.2 SSH 安全最佳实践8.2.1 重点基础知识8.2.2 重…

人工智能学习与实训笔记(六):神经网络之智能推荐系统

人工智能学习笔记汇总链接&#xff1a;人工智能学习与实训笔记汇总-CSDN博客 本篇目录 七、智能推荐系统处理 7.1 常用的推荐系统算法 7.2 如何实现推荐 7.3 基于飞桨实现的电影推荐模型 7.3.1 电影数据类型 7.3.2 数据处理 7.3.4 数据读取器 7.3.4 网络构建 7.3.4.1…

vue-ESlint (六)

代码规范 代码规范&#xff1a;一套写代码的约定规则。例如&#xff1a;"赋值符号的左右是否需要空格" "一句结束是否是要加;" . 老话说&#xff1a;"没有规矩不成方圆" → 正规的团队 需要 统一的编码风格 JavaScript Standard Style 规范说…

成本效能FinOps: Crane 部署

目录 一、实验 1.环境 2.安装kind 3.安装Crane 二、问题 1.脚本安装prometheus报错 2.查看集群信息失败 3.Helm添加grafana 报错 4.查看crane资源失败 5.prometheus部署时kube-state-metrics 拉取镜像显示ImagePullBackOff 6.Crane 功能与架构 一、实验 1.环境 &a…

智慧公厕的主要应用

在现代社会中&#xff0c;随着城市化进程的加速推进&#xff0c;公共卫生设施的建设和管理变得愈加重要。而智慧公厕作为一种新型城市公共设施&#xff0c;正以其智能化、高效化的特点&#xff0c;成为改善城市卫生环境的重要手段。智慧公厕运用物联网、互联网、大数据、云计算…

HAL/LL/STD STM32 U8g2库 +I2C SSD1306/sh1106 WouoUI磁贴案例

HAL/LL/STD STM32 U8g2库 I2C SSD1306/sh1106 WouoUI磁贴案例 &#x1f4cd;基于STM32F103C8T6 LL库驱动版本&#xff1a;https://gitee.com/chcsx/platform-test/tree/master/MDK-ARM&#x1f3ac;视频演示&#xff1a; WouoUI移植磁贴案例&#xff0c;新增确认弹窗 &#x1f…

无人驾驶LQR控制算法 c++ 实现

参考博客&#xff1a; &#xff08;1&#xff09;LQR的理解与运用 第一期——理解篇 &#xff08;2&#xff09;线性二次型调节器(LQR)原理详解 &#xff08;3&#xff09;LQR控制基本原理&#xff08;包括Riccati方程具体推导过程&#xff09; &#xff08;4&#xff09;【基础…

精品jsp+ssm鲜花销售管理系统-购物商城

《[含文档PPT源码等]精品jspssm鲜花销售管理系统[包运行成功]》该项目含有源码、文档、PPT、配套开发软件、软件安装教程、项目发布教程、包运行成功&#xff01; 使用技术&#xff1a; 开发语言&#xff1a;Java 框架&#xff1a;ssm 技术&#xff1a;JSP JDK版本&#x…

微信小程序如何配置服务器域名

目录 一、微信小程序 二、域名 三、服务器 四、如何配置服务器域名 一、微信小程序 微信小程序是一种轻量级的应用程序&#xff0c;用户无需下载安装即可使用&#xff0c;具有便捷、快捷的特点。微信小程序可以在微信内直接使用&#xff0c;无需离开微信即可完成各种功能&…

Leetcode - 周赛384

目录 一&#xff0c;3033. 修改矩阵 二&#xff0c;3035. 回文字符串的最大数量 三&#xff0c;3036. 匹配模式数组的子数组数目 II 一&#xff0c;3033. 修改矩阵 这道题直接暴力求解&#xff0c;先算出每一列的最大值&#xff0c;再将所有为-1的区域替换成该列的最大值&am…

人工智能学习与实训笔记(七):神经网络之推荐系统处理

九、模型压缩与知识蒸馏 出于对响应速度&#xff0c;存储大小和能耗的考虑&#xff0c;往往需要对大模型进行压缩。 模型压缩方法主要可以分为以下四类&#xff1a; 参数修剪和量化&#xff08;Parameter pruning and quantization&#xff09;&#xff1a;用于消除对模型表…

Java 学习和实践笔记(12)

这个就比较有意思了&#xff01;所有的事情&#xff0c;拆分完之后&#xff0c;都有且只有这三种状态流程&#xff01; //TIP To <b>Run</b> code, press <shortcut actionId"Run"/> or // click the <icon src"AllIcons.Actions.Execute&…

Vue源码系列讲解——模板编译篇【二】(整体运行流程)

目录 1. 整体流程 2. 回到源码 3. 总结 1. 整体流程 上篇文章中我们说了&#xff0c;在模板解析阶段主要做的工作是把用户在<template></template>标签内写的模板使用正则等方式解析成抽象语法树&#xff08;AST&#xff09;。而这一阶段在源码中对应解析器&…

《区块链公链数据分析简易速速上手小册》第7章:数据获取和分析的挑战(2024 最新版)

文章目录 7.1 数据准确性和完整性验证7.1.1 基础知识7.1.2 重点案例&#xff1a;验证加密货币交易数据准备工作实现步骤步骤1: 从 API 获取比特币交易数据步骤2: 数据转换和初步校验步骤3: 验证交易数据的格式和范围 结论 7.1.3 拓展案例 1&#xff1a;使用哈希校验数据完整性准…

NLP_Transformer架构

文章目录 Transformer架构剖析编码器-解码器架构各种注意力的应用Transformer中的自注意力Transformer中的多头自注意力Transformer中的编码器-解码器注意力Transformer中的注意力掩码和因果注意力 编码器的输入和位置编码编码器的内部结构编码器的输出和编码器-解码器的连接解…
最新文章