create-react-app 默认添加了 Jest
我们还需要另一个测试库帮助渲染组件react-testing-library
安装
npm install --save-dev @testing-library/react @testing-library/jest-dom
在components目录中新建Note.test.js文件(与Note组件在相同目录),编写测试代码
import React from 'react'import '@testing-library/jest-dom/extend-expect'import { render } from '@testing-library/react'import Note from './Note'test('renders content', () => {const note = {content: 'Component testing is done with react-testing-library',important: true,}const component = render(<Note note={note} />)expect(component.container).toHaveTextContent('Component testing is done with react-testing-library')})
如果执行命令**npm test**, 测试会在watch模式下执行,如果控制台报错需要安装watchman
如果不要在watch模式下执行,则使用命令**CI=true npm test**
之所以将测试文件与组件放在同一目录,是因为create-react-app创建项目时默认这样配置
Searching for content in a component
查找组件中的内容有多种不同的方法
import React from 'react'import '@testing-library/jest-dom/extend-expect'import { render } from '@testing-library/react'import Note from './Note'test('renders content', () => {const note = {content: 'Component testing is done with react-testing-library',important: true,}const component = render(<Note note={note} />)// method 1expect(component.container).toHaveTextContent('Component testing is done with react-testing-library')// method 2const element = component.getByText('Component testing is done with react-testing-library')expect(element).toBeDefined()// method 3const div = component.container.querySelector('.note')expect(div).toHaveTextContent('Component testing is done with react-testing-library')})
Debugging test
render返回的对象有一个debug方法,可以将组件的HTML打印到控制台
// ...test('renders content', () => {const note = {content: 'Component testing is done with react-testing-library',important: true,}const component = render(<Note note={note} />)component.debug()// ...}

还可以使用prettyDOM方法查找一小部分HTML, 在控制台打印出来
// ...import { prettyDOM } from '@testing-library/dom'test('renders content', () => {const note = {content: 'Component testing is done with react-testing-library',important: true,}const component = render(<Note note={note} />)const li = component.container.querySelector('li')console.log(prettyDOM(li))// ...}
Clicking buttons in tests
测试按钮被点击
// ...import { render, fireEvent } from '@testing-library/react'test('clicking the button calls event handler once', () => {const note = {content: 'Component testing is done with react-testing-library',important: true,}// 用jest定义mock函数const mockHandler = jest.fn()const component = render(<Note note={note} toggleImportance={mockHandler} />)const button = component.getByText('make not important')fireEvent.click(button)// 测试mock function是否只被调用了一次expect(mockHandler.mock.calls).toHaveLength(1)})
Tests for the Togglable component
测试Togglable组件
import React from 'react'import '@testing-library/jest-dom/extend-expect'import { render, fireEvent } from '@testing-library/react'import Togglable from './Togglable'describe('<Togglable />', () => {let componentbeforeEach(() => {component = render(<Togglable buttonLabel='show...'><div className='testDiv' /></Togglable>)})test('renders its children', () => {expect(component.container.querySelector('.testDiv')).toBeDefined()})test('at start the children are not displayed', () => {const div = component.container.querySelector('.togglableContent')expect(div).toHaveStyle('display: none')})test('after clicking the button, children are displayed', () => {const button = component.getByText('show...')fireEvent.click(button)const div = component.container.querySelector('.togglableContent')expect(div).not.toHaveStyle('display:none')})test('toggled content can be closed', () => {const button = component.getByText('show...')fireEvent.click(button)const closeButton = component.getByText('cancel')fireEvent.click(closeButton)const div = component.container.querySelector('.togglableContent')expect(div).toHaveStyle('display: none')})})
Testing the forms
fireEvent除了可以点击按钮,还可以模拟文本输入
import React from 'react'import { render, fireEvent } from '@testing-library/react'import '@testing-library/jest-dom/extend-expect'import NoteForm from './NoteForm'test('<NoteForm /> updates parent state and calls onSubmit', () => {const createNote = jest.fn()const component = render(<NoteForm createNote={createNote} />)const input = component.container.querySelector('input')const form = component.container.querySelector('form')fireEvent.change(input, {target: { value: 'testing of forms could be easier' },})fireEvent.submit(form)expect(createNote.mock.calls).toHaveLength(1)expect(createNote.mock.calls[0][0].content).toBe('testing of forms could be easier')})
Test coverage
使用如下命令查看测试覆盖范围,会生成一共原始的HTML报表
CI=true npm test -- --coverage
到目前为止做的都是单元测试unit tests,单元测试测试了单个组件的正确功能
如果要测试多个组件协作,需要从服务器模拟数据,进行集成测试integration tests,下一节将采用端到端测试 end to end tests整个应用。
