Open xiaochengzi6 opened 1 year ago
这里介绍的是 jest 和 rtl 库的使用 RTL 测试
这里介绍的是 jest 和 rtl 库的使用
查找 Dom 元素使用 getxxx、queryxxx、findxxxx 这三种查找方式
getxxx
queryxxx
findxxxx
通过在其后缀 All 之后就会查找多个元素,如果没有使用在后缀All的这种形式,查找元素存在多个的条件下就会抛出错误
参考一下 https://github.com/xiaochengzi6/Blog/issues/67 这里不经常用 所以简单的过了一下了解 这三种查找方式的区别就行
参考一下 https://github.com/xiaochengzi6/Blog/issues/67
这里不经常用 所以简单的过了一下了解 这三种查找方式的区别就行
// App 组件 function App() { return ( <form> <label htmlFor="title-input">Title</label> <input id="title-input" /> <label htmlFor="content-input">Content</label> <textarea id="content-input" /> <label htmlFor="tags-input">Tags</label> <input id="tags-input" /> <button type="submit">Submit</button> </form> ) } // App.test.js 测试组件 import {render, screen} from "@testing-ibrary/react" test('renders a form with title, content, tags, and a submit button', () => { render(<Editor />) screen.getByLabelText(/title/i) screen.getByLabelText(/content/i) screen.getByLabelText(/tags/i) screen.getByText(/submit/i) })
使用 userEvent 来触发事件,比如下面的点击
userEvent
// Editor.js function Editor() { function handleSubmit(e) { e.preventDefault() setIsSaving(true) } return ( <form onSubmit={handleSubmit}> <label htmlFor="title-input">Title</label> <input id="title-input" /> <button type="submit" disabled={isSaving}> Submit </button> </form> ) } // Editor.test.js test('renders a form with title, content, tags, and a submit button', () => { render(<Editor />) const submitButton = screen.getByText(/submit/i) userEvent.click(submitButton) expect(submitButton).toBeDisabled() })
有两个比较关键的 api render 和 act render 是渲染组件,调用 act 是进行对组件的更新
render
act
关于 act:https://legacy.reactjs.org/docs/test-utils.html#act
// useCounter.js import * as React from 'react' function useCounter({initialCount = 0, step = 1} = {}) { const [count, setCount] = React.useState(initialCount) const increment = () => setCount((c) => c + step) const decrement = () => setCount((c) => c - step) return {count, increment, decrement} } export {useCounter} // useCounter.test.js import * as React from 'react' import {render, act} from '@testing-library/react' import {useCounter} from './use-counter' test('exposes the count and increment/decrement functions', () => { let result // 由于是测试组件所以使用一个函数进行包裹 function TestComponent() { result = useCounter() return null } // 这种属于是测试组件的方法,这样测hook也没问题但没有使用 renderHook 那样优雅 render(<TestComponent />) expect(result.count).toBe(0) // 使用 act 进行组件的更新 act(() => result.increment()) expect(result.count).toBe(1) act(() => result.decrement()) expect(result.count).toBe(0) })
在 RTL 中有两种事件测试的包 userEvent 和 fireEvent
fireEvent
userEvent可以完整的模仿一套事件的触发,而 fireEvent是直接触发了事件,使用 dispatchEvnet方法。这就会导致在一前者的文件提交较大,后者更轻量化,在一些简单的事件操作下使用 fireEvent就行。
dispatchEvnet
diapatchEvent 参考:https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/dispatchEvent var event = new Event('build');
diapatchEvent 参考:https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/dispatchEvent
var event = new Event('build');
// Listen for the event. elem.addEventListener('build', function (e) { ... }, false);
// Dispatch the event. elem.dispatchEvent(event);
使用到 `userEvent` 事件去触发 常见的有以下几种 1. 鼠标事件: 点击、悬停、移入移出、选中 ~~~js import React from 'react' import {render, screen} from '@testing-library/react' import userEvent from '@testing-library/user-event' test('click', () => { render( <div> <label htmlFor="checkbox">Check</label> <input id="checkbox" type="checkbox" /> </div>, ) // 点击触发 userEvent.click(screen.getByText('Check')) expect(screen.getByLabelText('Check')).toBeChecked() })
使用 type 方法在 input 或 textarea去写入
type
input
textarea
import React from 'react' import {render, screen} from '@testing-library/react' import userEvent from '@testing-library/user-event' test('type', () => { render(<textarea />) // userEvent.type(screen.getByRole('textbox'), 'Hello,{enter}World!') expect(screen.getByRole('textbox')).toHaveValue('Hello,\nWorld!') })
这块东西有些多,没总结过来,具体的事件看官网吧...
参考:https://testing-library.com/docs/ecosystem-user-event
affterAll:大意就是在当前文件下运行完所有的测试结果后会掉用你通过 affterAll 传入的 函数 ,如果返回值是 promise 则会等待它处理完。
affterAll
promise
affterEach 在每个测试文件完成之后调用一次传入的函数,如果返回的是 promiseorgenerator 会等待它处理完
affterEach
generator
beforeAll 在运行此文件中的任何测试之前运行函数,如果返回的是 promiseorgenerator 会等待它处理完
beforeAll
beforeEach 在运行此文件中的每个测试之前运行一个函数, 在运行此文件中的每个测试之前运行一个函数
beforeEach
前面四种函数可以接收两个参数 (fn, timeout), 回调函数 fn 和 超时时间 默认为 5s 超过 5s 就会结束
fn
timeout
参考:https://www.jestjs.cn/docs/api#afterallfn-timeout
关于模拟函数参考这篇文章:https://github.com/xiaochengzi6/Blog/issues/66
在一些不支持使用 afterEach 这样自动注入到测试环境下的话需要自己去手动清除,但 jest、mocha 都会自动的去清除
afterEach
import {cleanup, render} from '@testing-library/react' import test from 'ava' // 比如在 ava 测试框架 // 需要手动清除 test.afterEach(cleanup)
在使用定时器时,在一些其它测试框架一般会采取虚拟定时器来模仿,为了确保在测试后不会改变原定时器功能尽量在测试后去恢复
在测试中所有的定时器都使用 虚拟定时器。
虚拟定时器
beforeEach(() => { jest.useFakeTimers() })
在测试结束后运行所有挂起的定时器并将虚拟定时器还原成定时器
定时器
afterEach(() => { // 运行所有挂起的定时器 jest.runOnlyPendingTimers() // 使用 jest.useRealTimers() })
一、获取 dom
查找 Dom 元素使用
getxxx
、queryxxx
、findxxxx
这三种查找方式通过在其后缀 All 之后就会查找多个元素,如果没有使用在后缀All的这种形式,查找元素存在多个的条件下就会抛出错误
二、触发事件
使用
userEvent
来触发事件,比如下面的点击三、测试 hook 组件
有两个比较关键的 api
render
和act
render
是渲染组件,调用act
是进行对组件的更新事件
在 RTL 中有两种事件测试的包
userEvent
和fireEvent
userEvent
可以完整的模仿一套事件的触发,而fireEvent
是直接触发了事件,使用dispatchEvnet
方法。这就会导致在一前者的文件提交较大,后者更轻量化,在一些简单的事件操作下使用fireEvent
就行。// Listen for the event. elem.addEventListener('build', function (e) { ... }, false);
// Dispatch the event. elem.dispatchEvent(event);
使用
type
方法在input
或textarea
去写入这块东西有些多,没总结过来,具体的事件看官网吧...
Jest 测试框架
全局钩子
affterAll
:大意就是在当前文件下运行完所有的测试结果后会掉用你通过affterAll
传入的 函数 ,如果返回值是promise
则会等待它处理完。affterEach
在每个测试文件完成之后调用一次传入的函数,如果返回的是promise
orgenerator
会等待它处理完beforeAll
在运行此文件中的任何测试之前运行函数,如果返回的是promise
orgenerator
会等待它处理完beforeEach
在运行此文件中的每个测试之前运行一个函数, 在运行此文件中的每个测试之前运行一个函数前面四种函数可以接收两个参数 (
fn
,timeout
), 回调函数 fn 和 超时时间 默认为 5s 超过 5s 就会结束模拟函数
关于模拟函数参考这篇文章:https://github.com/xiaochengzi6/Blog/issues/66
清除
在一些不支持使用
afterEach
这样自动注入到测试环境下的话需要自己去手动清除,但 jest、mocha 都会自动的去清除使用定时器需要注意的事项
在使用定时器时,在一些其它测试框架一般会采取虚拟定时器来模仿,为了确保在测试后不会改变原定时器功能尽量在测试后去恢复
在测试中所有的定时器都使用
虚拟定时器
。在测试结束后运行所有挂起的定时器并将
虚拟定时器
还原成定时器