使用Jest测试React项目中点击-路由跳转场景

3,663 阅读2分钟

场景

在使用Jest编写React项目单元测试时,会遇到比如点击某个按钮(查看详情),然后页面跳转到某个指定路由页面(详情页)的测试场景。基于BDD的思想,结合JestReact-Testing-Library,本文总结这种场景下单元测试编写思路以及遇到的问题。

// Component.tsx

import React from 'react'
import { useHistory } from 'react-router-dom'

const Component = () => {
    const history = useHistory()
    const handleClick = (evt: React.MouseEvent) => {
        history.push(`/pageDetails?id=${1}`)
    }
    return (
        <div>
            <button onClick={handleClick}>查看详情</button>
        </div>
    )
}

export default Component

思路

通过定位到某个按钮元素,模拟点击按钮后,断言

  • 点击事件对应的函数被调用
  • 当前路由是我们指定跳转后的路由pathname
  • 路由如携带参数search为我们模拟的参数。
// component.test.js

import React from 'react'
import { render, screen, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event'
import {createMemoryHistory} from 'history'
import Component from './component'

describe('测试组件页', () => {
  it('点击查看详情按钮,跳转到详情页', () => {
      render(<Component />)
      const history = createMemoryHistory()
      
      // 定位元素模拟点击按钮
      act(() => {
        userEvent.click(screen.getByText('详细信息'))
      })
      
      // 断言
      expect(history.location.pathname).toBe("/pageDetails") // 断言指定路由
      expect(history.location.search).toBe("?id=1") // 断言携带参数
  })

遇到的问题

  • 直接运行写好的单元测试时,点击按钮调用的是我们组件真实代码绑定的函数handleClick。会出现测试不通过Expected: "/pageDetails";Received: "/"
  • 问题原因是
    • createMemoryHistory在执行时获取的是默认的路径/
  • 方案一可以是把点击跳转调用的函数放在单元测试中进行,即创建一个模拟函数,再运行测试。参考下文代码实现。
  • 方案二也可以是使用Router包裹Component组件,在Routerprops传入history后,模拟点击,再断言。参考createMemoryHistory

方案一代码实现

// component.test.js

import React from 'react'
import { render, screen, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event'
import {createMemoryHistory} from 'history'
import Component from './component'

describe('测试组件页', () => {
    it('点击查看详情按钮,跳转到详情页', () => {
        render(<Component />)

        const history = createMemoryHistory()
        const clickHandler = jest.fn(evt => { // 创建模拟点击事件函数
          evt.preventDefault()
          evt.stopPropagation()
          history.push('/pageDetails?id=1')
        })
        screen.getByText('查看详情').onclick = evt => clickHandler(evt) // 将模拟点击事件函数绑定到按钮

        act(() => {
          userEvent.click(screen.getByText('详细信息'))
        })

        expect(clickHandler).toHaveBeenCalled() // 断言点击事件被调用
        expect(history.location.pathname).toBe("/pageDetails") // 断言指定路由
        expect(history.location.search).toBe("?id=6") // 断言携带参数
    })
})

参考

createMemoryHistory