React 18 已经正式发布了,作为了组件库也需要更新自己的测试用例,但是 enzyme 已经停止维护了,虽然有社区的 @cfaester/enzyme-adapter-react-18 库,但是一些复杂 Dom 渲染经常出错。比如 ProTable,这时候就需要 @testing-library 了。

  1. import Adapter from '@cfaester/enzyme-adapter-react-18';
  2. Enzyme.configure({ adapter: new Adapter() });

@testing-library

@testing-library 是一个比较新的库,它的思路是站在用户角度来进行测试,所以 API 与传统库的有很大不同。
image.png

他没有传统的 find,或者 query API,你要测试都需要通过用户可见的东西,比如文字,比如 role,或者 displayValue。省去了寻找 dom 的烦恼。
image.png

如何替换?

因为与 enzyme 的写法不同,我们要修改很多写法,其中最多的就是 find。虽然 testing-library 没有 find,但是可以拿到原生的dom来使用。这里就是一个很好的例子。
image.png

Find

我们可以通过原生的 querySelectorAll 来代替 find。当然这个是最偷懒的情况。最好的情况是我们通过 text 来找到这个dom,就像一个用户真的在用一样。

image.png
比如我们要找到提交按钮,可以用 await (await wrapper.findByText('提 交')).click(); 来找到并且点击。找到的按钮是一个模拟的 html dom,我们也可以判断它的状态,上面这个测试用例就是在测试 onFinish 种提交按钮点击之后应该出现 loading 效果。
相比于原来的代码,这里的代码更加精简也更加好赌,就像一个用户再点我们的代码一样。其中最好用的就是 findAllByDisplayValue,我们可以根据他找到当前的输入框,比原来的根据id来找,能节省很多的代码。

image.png

Simulate

实际的测试用例中 simulate 是或不可缺的,我们需要用它来测试各种事件,在 testing-library 中,我们推荐是用 .click 来触发点击事件,别的事件可以通过 fireEvent 来实现。

image.png

image.png

自带了很多事件,我们可以在类型里面找到,常用的有 change,mouseDown,hover,focus 等,使用起来与 simulate 基本相同。

测试网路请求

testing-library 提供了一个相当易用的网络请求解决方案,可以和 testing-library 配合的很好。

  1. import React from 'react'
  2. import {rest} from 'msw'
  3. import {setupServer} from 'msw/node'
  4. import {render, fireEvent, waitFor, screen} from '@testing-library/react'
  5. import '@testing-library/jest-dom'
  6. import Fetch from '../fetch'
  7. const server = setupServer(
  8. rest.get('/greeting', (req, res, ctx) => {
  9. return res(ctx.json({greeting: 'hello there'}))
  10. }),
  11. )
  12. beforeAll(() => server.listen())
  13. afterEach(() => server.resetHandlers())
  14. afterAll(() => server.close())
  15. test('loads and displays greeting', async () => {
  16. render(<Fetch url="/greeting" />)
  17. fireEvent.click(screen.getByText('Load Greeting'))
  18. await waitFor(() => screen.getByRole('heading'))
  19. expect(screen.getByRole('heading')).toHaveTextContent('hello there')
  20. expect(screen.getByRole('button')).toBeDisabled()
  21. })

使用体验非常像使用 express 里面写一个路由而且可以基于每个 it 来进行设置,方便许多。

最后官方也提供了一些 场景来判断我们的迁移方式,一些高阶用法可以看看这个里面。

单独测试 hooks

renderHook 是 testing-library 另外一个比较亮的功能,可以很好的测试一些复杂hooks,构造特殊的示例。

  1. import {renderHook} from '@testing-library/react'
  2. const {result} = renderHook(() => {
  3. const [name, setName] = useState('')
  4. React.useEffect(() => {
  5. setName('Alice')
  6. }, [])
  7. return name
  8. })
  9. expect(result.current).toBe('Alice')

current 是你最近返回值的引用,所以下次更新之后他还是会变的这个要注意一下。

React 18 迁移要注意什么?

其实超级简单,如果你没用一些废弃很久的API,几乎没什么成本。对我而言最开始的是 useMergedState 已经可以废弃了,再也不用担心黄色的警告了。

image.png

我们要做的也很简单,将 antd 升级到 4.20.1, 然后升级 bigfish 到 4。一般而言这样就可以启动了。启动之后我们需要检查一下我们项目中的 addEventListener 和 settimeout 有没有即时清空,如果没有可能 会导致多次监听。

还有使用了 useEffect 但是没有配置监听的,大概率会导致死循环。

最难的是使用了 findDOMNode 的,几乎必挂,但是我们的项目中应该没有。