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 1
expect(component.container).toHaveTextContent(
'Component testing is done with react-testing-library'
)
// method 2
const element = component.getByText('Component testing is done with react-testing-library')
expect(element).toBeDefined()
// method 3
const 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 component
beforeEach(() => {
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整个应用。