iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 18
1
Modern Web

循序漸進學習 Javascript 測試系列 第 18

Day 18 測試 React 元件:測試元件的 Event Handlers

目前為止我們已經了解該如何使用 React Testing Library 的 renderdebug

測試元件的狀態了。 今天我們將繼續探索如何測試元件的 Event Handlers。

使用 fireEvent 測試元件的 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'

  • 步驟一:引入 React Testing Library 的 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 在測試裡觸發事件

使用 User Event 強化 Event Handlers 的測試

在 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 所觸發的一系列事件


上一篇
Day 17 測試 React 元件:使用 React Testing Library 測試元件的狀態
下一篇
Day 19 測試 React 元件:Mock HTTP Requests
系列文
循序漸進學習 Javascript 測試30

尚未有邦友留言

立即登入留言