目前為止我們已經了解該如何使用 React Testing Library 的 render
跟 debug
來
測試元件的狀態了。 今天我們將繼續探索如何測試元件的 Event Handlers。
先看一下元件的完整程式碼:
favorite-number.js
function FavoriteNumber({min = 1, max = 9}) {
const [number, setNumber] = React.useState(0)
const [numberEntered, setNumberEntered] = React.useState(false)
function handleChange(event) {
setNumber(Number(event.target.value))
setNumberEntered(true)
}
const isValid = !numberEntered || (number >= min && number <= max)
return (
<div>
<label htmlFor="favorite-number">Favorite Number</label>
<input
id="favorite-number"
type="number"
value={number}
onChange={handleChange}
/>
{isValid ? null : <div role="alert">The number is invalid</div>}
</div>
)
}
元件裡面有一個 handleChange
event handler,當 input 觸發 onChange 時,它會去改變現在的狀態 (number 及 numberEntered),元件依據現在的狀態得到 isValid
的值,當 isValid
是 false 的時候,會 render <div role="alert">The number is invalid</div>
。
現在我們要來測試一下觸發 handleChange
這個 event handler,並輸入一個 invalid 的值,驗證是否會如預期 render <div role="alert">The number is invalid</div>
出現。
我們在 __tests__/state.js
檔案裡面來寫這個測試,將測試命名為 'entering an invalid value shows an error message'
:
fireEvent
來觸發事件。fireEvent.change()
來觸發 change 事件。傳入第一個參數 input
及 第二個參數 {target: {value: '10'}}
,代表要在 input 觸發一個 change 事件,事件觸發時的 target.value 為 '10'
。( '10'
是元件 invalid 的 number )tests/state.js
import React from 'react'
import {render, fireEvent} from '@testing-library/react' // 引入 fireEvent
import {FavoriteNumber} from '../favorite-number'
test('entering an invalid value shows an error message', () => {
const {getByLabelText} = render(<FavoriteNumber />) //
const input = getByLabelText(/favorite number/i)
fireEvent.change(input, {target: {value: '10'}}) // event.target.value 為 '10'
})
getByRole('alert')
驗證 <div role="alert">The number is invalid</div>
是否如預期出現。並使用 .toHaveTextContent(/the number is invalid/i)
驗證裡面的文字是不是 The number is invalid
( regex /the number is invalid/i
代表忽略大小寫)tests/state.js
import React from 'react'
import {render, fireEvent} from '@testing-library/react'
import {FavoriteNumber} from '../favorite-number'
test('entering an invalid value shows an error message', () => {
const {getByLabelText, getByRole} = render(<FavoriteNumber />) // 使用 getByRole
const input = getByLabelText(/favorite number/i)
fireEvent.change(input, {target: {value: '10'}})
expect(getByRole('alert')).toHaveTextContent(/the number is invalid/i) // 驗證有出現 alert
})
到這邊我們暫時完成了一個基本的 Event Handler 測試。
Tips:使用 React Testing Library 的 fireEvent 在測試裡觸發事件
在 React 中, onChange
是一個合成事件,用在監聽 input、textarea、select、radio 等 form control 元素的變化,簡單來說,就是使用者操作改變 form control 元素的狀態時,會觸發 onChange
。
我們前面的 Event Handler 測試使用了 fireEvent.change()
只觸發了瀏覽器的 change
事件,但實際上使用者操作改變 input 時,會觸發一系列的瀏覽器事件,keydown、keyup、input、change 等等。如果我們只在測試觸發 change
事件,無法真實模擬使用者操作 input 時的情境。
使用 testing library 家族的 user-event
,可以幫助我們在測試中貼近使用者的操作來觸發事件:
npm install --save-dev @testing-library/user-event
或
yarn add --dev @testing-library/user-event
import 進來到我們的測試中:
import user from '@testing-library/user-event'
使用 type()
模擬使用者輸入 input 時觸發的事件,第一個參數傳入 form control 元素,第二個參數傳入欲輸入的值:
tests/state.js
import user from '@testing-library/user-event'
test('entering an invalid value shows an error message', () => {
const {getByLabelText, getByRole} = render(<FavoriteNumber />)
const input = getByLabelText(/favorite number/i)
user.type(input, '10') // 使用 user.type()
// 移除 fireEvent.change(input, {target: {value: '10'}})
expect(getByRole('alert')).toHaveTextContent(/the number is invalid/i)
})
其實 user-event
背後也是使用 fireEvent
來觸發事件,只是它可以讓我們在測試時更方便模擬使用者操作,幫我們觸發使用者操作時的一系列事件。
Tips:使用 User Event 的
type()
API 模擬使用者真實輸入 input 所觸發的一系列事件