使用 Jest 和 react-testing-library 測試你的第一個 React 元件

開始測試 React 元件最簡單的方法是使用快照測試。快照測試是一種讓你在獨立環境中測試元件的技術。

如果你對軟體測試很熟悉,這就像你為類別進行單元測試一樣:你測試每個元件的功能。

我假設你已經使用 create-react-app 創建了一個 React 應用,這個應用已經預先安裝了我們需要的測試套件 Jest

讓我們從一個簡單的測試開始。CodeSandbox 是一個很好的環境來嘗試這個。在 CodeSandbox 中創建一個 React sandbox,並在 components 文件夾中創建一個 App.js 元件,然後添加一個 App.test.js 文件。

import React from 'react'

export default function App() {
 return (
   <div className="App">
     <h1>Hello CodeSandbox</h1>
     <h2>Start editing to see some magic happen!</h2>
   </div>
 )
}

我們的第一個測試很簡單:

test('First test', () => {
 expect(true).toBeTruthy()
})

當 CodeSandbox 檢測到測試文件時,它會自動執行這些測試,你可以點擊視圖底部的 “Tests” 按鈕來查看測試結果:

一個測試文件可以包含多個測試:

現在讓我們做一些更有用的事情,實際上測試一個 React 元件。我們現在只有一個沒有實際用途的 App 元件,所以讓我們先設置一個具有更多功能的小應用程式環境:我們之前構建的計數器應用程式。如果你跳過了這部分,你可以返回並閱讀我們是如何構建它的,但為了方便參考,我在這裡再次添加一遍。

它僅由兩個元件組成:App 和 Button。創建 App.js 文件:

import React, { useState } from 'react'
import Button from './Button'

const App = () => {
 const [count, setCount] = useState(0)

 const incrementCount = increment => {
 setCount(count + increment)
 }

 return (
   <div>
     <Button increment={1} onClickFunction={incrementCount} />
     <Button increment={10} onClickFunction={incrementCount} />
     <Button increment={100} onClickFunction={incrementCount} />
     <Button increment={1000} onClickFunction={incrementCount} />
     <span>{count}</span>
   </div>
 )
}

export default App

Button.js 文件:

import React from 'react'

const Button = ({ increment, onClickFunction }) => {
 const handleClick = () => {
 onClickFunction(increment)
 }
 return <button onClick={handleClick}>+{increment}</button>
}

export default Button

我們將使用 react-testing-library,這是一個很好的幫助工具,它可以讓我們檢查每個元件的輸出並對其應用事件。你可以在 這裡 了解更多相關信息,或觀看 這個視頻

讓我們先測試 Button 元件。

我們首先從 react-testing-library 中導入 renderfireEvent,兩個輔助工具。第一個可以讓我們渲染 JSX,第二個可以讓我們釋放組件上的事件。

在與 Button.js 相同的文件夾中創建一個 Button.test.js 文件。

import React from 'react'
import { render, fireEvent } from 'react-testing-library'
import Button from './Button'

按鈕在應用程式中用於接受點擊事件,然後它們會調用傳遞給 onClickFunction 屬性的函數。我們添加了一個 count 變量,然後創建一個將它增加的函數:

let count

const incrementCount = increment => {
 count += increment
}

現在是進行實際測試的時候。我們首先將 count 初始化為 0,然後渲染一個 +1 的 Button 元件,傳遞 1increment,並將我們的 incrementCount 函數傳遞給 onClickFunction

接著我們獲取元件的第一個子元素的內容,並檢查它輸出 +1

然後我們點擊按鈕,並檢查 count 從 0 變成 1:

test('+1 Button works', () => {
 count = 0
 const { container } = render(
   <Button increment={1} onClickFunction={incrementCount} />
 )
 const button = container.firstChild
 expect(button.textContent).toBe('+1')
 expect(count).toBe(0)
 fireEvent.click(button)
 expect(count).toBe(1)
})

類似地,我們測試了一個 +100 的按鈕,這次檢查輸出是否為 +100,並且通過按鈕點擊將 count 增加了 100:

test('+100 Button works', () => {
 count = 0
 const { container } = render(
   <Button increment={100} onClickFunction={incrementCount} />
 )
 const button = container.firstChild
 expect(button.textContent).toBe('+100')
 expect(count).toBe(0)
 fireEvent.click(button)
 expect(count).toBe(100)
})

現在讓我們測試 App 元件。它顯示了 4 個按鈕和頁面上的結果。我們可以檢查每個按鈕,看看結果在點擊它們時是否增加,也可以點擊多次:

import React from 'react'
import { render, fireEvent } from 'react-testing-library'
import App from './App'

test('App works', () => {
 const { container } = render(<App />)
 console.log(container)
 const buttons = container.querySelectorAll('button')

 expect(buttons[0].textContent).toBe('+1')
 expect(buttons[1].textContent).toBe('+10')
 expect(buttons[2].textContent).toBe('+100')
 expect(buttons[3].textContent).toBe('+1000')

 const result = container.querySelector('span')
 expect(result.textContent).toBe('0')
 fireEvent.click(buttons[0])
 expect(result.textContent).toBe('1')
 fireEvent.click(buttons[1])
 expect(result.textContent).toBe('11')
 fireEvent.click(buttons[2])
 expect(result.textContent).toBe('111')
 fireEvent.click(buttons[3])
 expect(result.textContent).toBe('1111')
 fireEvent.click(buttons[2])
 expect(result.textContent).toBe('1211')
 fireEvent.click(buttons[1])
 expect(result.textContent).toBe('1221')
 fireEvent.click(buttons[0])
 expect(result.textContent).toBe('1222')
})

在這個 CodeSandbox 上檢查代碼工作: https://codesandbox.io/s/pprl4y0wq