React 18 已经正式发布了,作为了组件库也需要更新自己的测试用例,但是 enzyme 已经停止维护了,虽然有社区的 @cfaester/enzyme-adapter-react-18
库,但是一些复杂 Dom 渲染经常出错。比如 ProTable,这时候就需要 @testing-library 了。
import Adapter from '@cfaester/enzyme-adapter-react-18';
Enzyme.configure({ adapter: new Adapter() });
@testing-library
@testing-library 是一个比较新的库,它的思路是站在用户角度来进行测试,所以 API 与传统库的有很大不同。
他没有传统的 find,或者 query API,你要测试都需要通过用户可见的东西,比如文字,比如 role,或者 displayValue。省去了寻找 dom 的烦恼。
如何替换?
因为与 enzyme 的写法不同,我们要修改很多写法,其中最多的就是 find。虽然 testing-library 没有 find,但是可以拿到原生的dom来使用。这里就是一个很好的例子。
Find
我们可以通过原生的 querySelectorAll 来代替 find。当然这个是最偷懒的情况。最好的情况是我们通过 text 来找到这个dom,就像一个用户真的在用一样。
比如我们要找到提交按钮,可以用 await (await wrapper.findByText('提 交')).click();
来找到并且点击。找到的按钮是一个模拟的 html dom,我们也可以判断它的状态,上面这个测试用例就是在测试 onFinish 种提交按钮点击之后应该出现 loading 效果。
相比于原来的代码,这里的代码更加精简也更加好赌,就像一个用户再点我们的代码一样。其中最好用的就是 findAllByDisplayValue,我们可以根据他找到当前的输入框,比原来的根据id来找,能节省很多的代码。
Simulate
实际的测试用例中 simulate 是或不可缺的,我们需要用它来测试各种事件,在 testing-library 中,我们推荐是用 .click 来触发点击事件,别的事件可以通过 fireEvent 来实现。
自带了很多事件,我们可以在类型里面找到,常用的有 change,mouseDown,hover,focus 等,使用起来与 simulate 基本相同。
测试网路请求
testing-library 提供了一个相当易用的网络请求解决方案,可以和 testing-library 配合的很好。
import React from 'react'
import {rest} from 'msw'
import {setupServer} from 'msw/node'
import {render, fireEvent, waitFor, screen} from '@testing-library/react'
import '@testing-library/jest-dom'
import Fetch from '../fetch'
const server = setupServer(
rest.get('/greeting', (req, res, ctx) => {
return res(ctx.json({greeting: 'hello there'}))
}),
)
beforeAll(() => server.listen())
afterEach(() => server.resetHandlers())
afterAll(() => server.close())
test('loads and displays greeting', async () => {
render(<Fetch url="/greeting" />)
fireEvent.click(screen.getByText('Load Greeting'))
await waitFor(() => screen.getByRole('heading'))
expect(screen.getByRole('heading')).toHaveTextContent('hello there')
expect(screen.getByRole('button')).toBeDisabled()
})
使用体验非常像使用 express 里面写一个路由而且可以基于每个 it 来进行设置,方便许多。
最后官方也提供了一些 场景来判断我们的迁移方式,一些高阶用法可以看看这个里面。
单独测试 hooks
renderHook 是 testing-library 另外一个比较亮的功能,可以很好的测试一些复杂hooks,构造特殊的示例。
import {renderHook} from '@testing-library/react'
const {result} = renderHook(() => {
const [name, setName] = useState('')
React.useEffect(() => {
setName('Alice')
}, [])
return name
})
expect(result.current).toBe('Alice')
current 是你最近返回值的引用,所以下次更新之后他还是会变的这个要注意一下。
React 18 迁移要注意什么?
其实超级简单,如果你没用一些废弃很久的API,几乎没什么成本。对我而言最开始的是 useMergedState 已经可以废弃了,再也不用担心黄色的警告了。
我们要做的也很简单,将 antd 升级到 4.20.1, 然后升级 bigfish 到 4。一般而言这样就可以启动了。启动之后我们需要检查一下我们项目中的 addEventListener 和 settimeout 有没有即时清空,如果没有可能 会导致多次监听。
还有使用了 useEffect 但是没有配置监听的,大概率会导致死循环。
最难的是使用了 findDOMNode 的,几乎必挂,但是我们的项目中应该没有。