FullStackOpen Part5-c Testing React apps メモ
Reactアプリのテストツールとして、テスト用にコンポーネントをレンダーするreact-testing-libraryやjest-domを使用する
npm install --save-dev @testing-library/react @testing-library/jest-dom
Rendering the component for tests
テストはこんな感じで記述
./components/Note.test.js
renderで描画し、screen.getByTextなどでアクセスする
import React from 'react'
import '@testing-library/jest-dom/extend-expect'
import { render, screen } from '@testing-library/react'
import Note from './Note'
test('renders content', () => {
const note = {
content: 'Component testing is done with react-testing-library',
important: true
}
render(<Note note={note} />)
const element = screen.getByText('Component testing is done with react-testing-library')
expect(element).toBeDefined()
})
Running tests
テストを走らせるには以下のコマンド
$env:CI=$true; npm test
Test file location
テスト対象のコンポーネントと同じフォルダに置く方法と、tests用フォルダを作ってそこにまとめておく方法の二通がよく用いられる
Searching for content in a component
テスト対象を探す方法はいくつかある
テキストで探す: screen.getByText()
CSSセレクタで探す :
container.querySelector()
import React from 'react'
import '@testing-library/jest-dom/extend-expect'
import { render, screen } 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 { container } = render(<Note note={note} />)
const div = container.querySelector('.note')
expect(div).toHaveTextContent(
'Component testing is done with react-testing-library'
)
})
テスト用にIDを割り当てて探す: getByTestId
Debugging tests
screen.debug()を使ってHTMLを確認できる
screen.debug(element)とするとそのエレメントのみ抽出して確認できる
Clicking buttons in tests
user-eventを使ってクリックのテストを行う
npm install --save-dev @testing-library/user-event
モック関数を作成し、ボタンをクリックしたら一回だけモック関数が呼ばれていることを確認する
import React from 'react'
import '@testing-library/jest-dom/extend-expect'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'import Note from './Note'
// ...
test('clicking the button calls event handler once', async () => {
const note = {
content: 'Component testing is done with react-testing-library',
important: true
}
const mockHandler = jest.fn()
render(
<Note note={note} toggleImportance={mockHandler} />
)
const user = userEvent.setup()
const button = screen.getByText('make not important')
await user.click(button)
expect(mockHandler.mock.calls).toHaveLength(1)
})
Tests for the Togglable component
Togglableの場合は以下のようにテストを作成(Togglable.test.js)
buttonを押してトグルできるかチェック
- toHaveStyle関数を使ってdisplay: noneになっているか確認している
import React from 'react'
import '@testing-library/jest-dom/extend-expect'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import Togglable from './Togglable'
describe('<Togglable />', () => {
let container
beforeEach(() => {
container = render(
<Togglable buttonLabel='show...'>
<div className='testDiv' >
togglable content
</div>
</Togglable>
).container
})
test('renders its children', async () => {
await screen.findAllByText('togglable content')
})
test('at the start the children are not displayed', () => {
const div = container.querySelector('.togglableContent')
expect(div).toHaveStyle('display: none')
})
test('after clicking the button, children are displayed', async () => {
const user = userEvent.setup()
const button = screen.getByText('show...')
await user.click(button)
const div = container.querySelector('.togglableContent')
expect(div).not.toHaveStyle('display: none')
})
test('toggled content can be closed', async () => {
const user = userEvent.setup()
const button = screen.getByText('show...')
await user.click(button)
const closeButton = screen.getByText('Cancel')
await user.click(closeButton)
const div = container.querySelector('.togglableContent')
expect(div).toHaveStyle('display: none')
})
})
Testing the forms
フォームの入力テストもuser-eventを使用する
getByRole('textbox')でinputフィールドを見つけてくる
user.type(input, 'wowbar')で入力
//NoteForm.test.js
import React from 'react'
import { render, screen } from '@testing-library/react'
import '@testing-library/jest-dom/extend-expect'
import NoteForm from './NoteForm'
import userEvent from '@testing-library/user-event'
test('<NoteForm /> updates parent state and calls onSubmit', async () => {
const createNote = jest.fn()
const user = userEvent.setup()
render(<NoteForm createNote={createNote} />)
const input = screen.getByRole('textbox')
const sendButton = screen.getByText('save')
await user.type(input, 'testing a form...')
await user.click(sendButton)
expect(createNote.mock.calls).toHaveLength(1)
expect(createNote.mock.calls[0][0].content).toBe('testing a form...')
})
About finding the elements
例えば複数のInputがフォーム中にある場合、getByRoleだとエラーを返してしまう。
その時に使える方法は:
-getAllByRoleで複数のInputを配列としてとる方法
-getByPlaceholderTextで検索してとる方法
-querySelectorを使用してCSSセレクタを使って取ってくる方法
Test coverage
テスト実施範囲のレポート以下のコマンドで作成できる
npm test -- --coverage
生成したレポートはプロジェクトの./coverage/lcov-report/index.htmlから見ることができる
Frontend integration tests
ユニットテスト(コンポーネントごとの機能検証)だけでは複雑なシナリオの検証はできない。
そのためEnd to endな検証をして正常に動作するか確認する
Snapshot Testing
Snapshot testingはJestのテスト方法の一つであり、コンポーネントにある変更を加えたときに、それによって生じたHTMLコードの変化を監視し、それがバグによるものなのか、それとも意図したものなのかをトラッキングするもの