React 测试库 - 入门

React Testing Library 是在 DOM Testing Library的基础上添加了用于处理 React 组件的 API。

使用 Create React App 创建的项目默认支持 React Testing Library。如果不是这样,你可以通过 npm 添加它,如下所示:

npm install --save-dev @testing-library/react

// yarn
yarn add --dev @testing-library/react

React Testing Library 是测试 React 组件的轻量级解决方案。它在r eact-dom 和 react-dom/test-utils 之上提供了轻量级的实用函数,从而鼓励更好的测试实践。其主要指导原则是:

您的测试与软件的使用方式越相似,它们就越能给您带来信心。

因此,你的测试将使用实际的 DOM 节点,而不是处理已渲染的 React 组件实例。该库提供的实用程序使用户能够以相同的方式查询 DOM。通过标签文本找到表单元素(就像用户一样),通过文本找到链接和按钮(就像用户一样)。它还公开了一种推荐的方式,即通过 data-testid 查找元素,作为对文本内容和标签没有意义或不切实际的元素的“逃生舱口”。

这个库鼓励你的应用程序更加便于访问,并允许你的测试更接近用户使用你的组件的方式,这样你的测试就可以让你更有信心你的应用程序在真实用户使用时会起作用。

这个库是 Enzyme 的替代品。虽然你可以使用 Enzyme 本身遵循这些指导原则,但因为 Enzyme 提供了许多额外的实用工具(这些实用工具有助于测试实现细节),因此很难强制执行这一点。你可以在常见问题解答中了解更多信息。

快速入门

这是最基本的设置,可以让你开始操作。如果你想要看到每一行所执行的操作描述,请滚动至带有注释的版本。滚动至“完整示例”以查看更高级的测试设置。

被测试的组件 Fetch:

import React, {useState, useReducer} from 'react'
import axios from 'axios'

const initialState = {
  error: null,
  greeting: null,
}

function greetingReducer(state, action) {
  switch (action.type) {
    case 'SUCCESS': {
      return {
        error: null,
        greeting: action.greeting,
      }
    }
    case 'ERROR': {
      return {
        error: action.error,
        greeting: null,
      }
    }
    default: {
      return state
    }
  }
}

export default function Fetch({url}) {
  const [{error, greeting}, dispatch] = useReducer(
    greetingReducer,
    initialState,
  )
  const [buttonClicked, setButtonClicked] = useState(false)

  const fetchGreeting = async url =>
    axios
      .get(url)
      .then(response => {
        const {data} = response
        const {greeting} = data
        dispatch({type: 'SUCCESS', greeting})
        setButtonClicked(true)
      })
      .catch(error => {
        dispatch({type: 'ERROR', error})
      })

  const buttonText = buttonClicked ? 'Ok' : 'Load Greeting'

  return (
    <div>
      <button onClick={() => fetchGreeting(url)} disabled={buttonClicked}>
        {buttonText}
      </button>
      {greeting && <h1>{greeting}</h1>}
      {error && <p role="alert">Oops, failed to fetch!</p>}
    </div>
  )
}
import React from 'react'
import {rest} from 'msw'
import {setupServer} from 'msw/node'
import {render, fireEvent, 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 screen.findByRole('heading')

  expect(screen.getByRole('heading')).toHaveTextContent('hello there')
  expect(screen.getByRole('button')).toBeDisabled()
})

test('handles server error', async () => {
  server.use(
    rest.get('/greeting', (req, res, ctx) => {
      return res(ctx.status(500))
    }),
  )

  render(<Fetch url="/greeting" />)

  fireEvent.click(screen.getByText('Load Greeting'))

  await screen.findByRole('alert')

  expect(screen.getByRole('alert')).toHaveTextContent('Oops, failed to fetch!')
  expect(screen.getByRole('button')).not.toBeDisabled()
})

建议在测试中声明式地使用 Mock Service Worker 库来模拟API通信,而不是使用 window.fetch,或者依赖第三方适配器。

逐步讲解

1、导入依赖

// import dependencies
import React from 'react'

// import API mocking utilities from Mock Service Worker
import {rest} from 'msw'
import {setupServer} from 'msw/node'

// import react-testing methods
import {render, fireEvent, screen} from '@testing-library/react'

// add custom jest matchers from jest-dom
import '@testing-library/jest-dom'
// the component to test
import Fetch from '../fetch'


test('loads and displays greeting', async () => {
  // Arrange
  // Act
  // Assert
})

2、Mock

使用 msw 的 setupServer 函数来模拟我们测试组件发出的API请求。

// declare which API requests to mock
const server = setupServer(
  // capture "GET /greeting" requests
  rest.get('/greeting', (req, res, ctx) => {
    // respond using a mocked JSON body
    return res(ctx.json({greeting: 'hello there'}))
  }),
)

// establish API mocking before all tests
beforeAll(() => server.listen())
// reset any request handlers that are declared as a part of our tests
// (i.e. for testing one-time error scenarios)
afterEach(() => server.resetHandlers())
// clean up once the tests are done
afterAll(() => server.close())

// ...

test('handles server error', async () => {
  server.use(
    // override the initial "GET /greeting" request handler
    // to return a 500 Server Error
    rest.get('/greeting', (req, res, ctx) => {
      return res(ctx.status(500))
    }),
  )

  // ...
})

3、遵守 3A 规则

单元测试中的3A规则是指Arrange、Act、Assert,分别代表一个合格的单元测试方法的三个阶段,即事先的准备、测试方法的实际调用、针对返回值的断言。

3.1、Arrange

render 方法将 React 元素渲染到 DOM 中。

render(<Fetch url="/greeting" />)
3.2、Act

fireEvent 方法允许您触发事件以模拟用户操作。

fireEvent.click(screen.getByText('Load Greeting'))

// wait until the `get` request promise resolves and
// the component calls setState and re-renders,
// throwing an error if it cannot find an element
await screen.findByRole('heading')
3.3、Assert

针对返回值的断言。

// assert that the alert message is correct using
// toHaveTextContent, a custom matcher from jest-dom.
expect(screen.getByRole('alert')).toHaveTextContent('Oops, failed to fetch!')

// assert that the button is not disabled using
// toBeDisabled, a custom matcher from jest-dom.
expect(screen.getByRole('button')).not.toBeDisabled()

  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值