背景
在平時開發(fā)過程中,可能大部分開發(fā)同學(xué)是忽略對react組件進(jìn)行一些自動化測試工作的,更多可能是以實現(xiàn)功能為目標(biāo),測試就交給測試同學(xué)。如果能做一些單元測試,短期看可能會增加工作量,長期來看,實際上能夠避免一些低級錯誤,提高開發(fā)效率,降低故障率。本文基于create-react-app搭建前端項目,使用Jest 和 @testing-library/react進(jìn)行組件測試。
最簡單的react組件測試方式就是做快照測試,能夠獨立的測試每一個組件,有點類似于單元測試中對classes進(jìn)行測試。
準(zhǔn)備前端項目
我們通過create-react-app創(chuàng)建一個前端測試項目
npx create-react-app react-components-test
耐心等待項目創(chuàng)建完成。項目創(chuàng)建完成后,我們通過cd react-components-test指令進(jìn)入到項目目錄下,我們可以看到已經(jīng)有一個測試的js——App.test.js:

之后執(zhí)行npm test,就可以看到一個測試結(jié)果:

在這個結(jié)果中,我們可以看到測試文件幾個,然后測試用例通過數(shù)量是多少,測試花費的時間等等信息。
難度上升了
接下來,我們做一些有實際用處的測試工作。我們先簡單創(chuàng)建一個小的前端應(yīng)用,添加一些方法功能,讓測試顯的更真實一點。
首先,讓我們添加一個Button組件,這個組件的功能就是點擊之后,就會加上button上設(shè)置的數(shù)值。這個組件有兩個傳參increment(設(shè)置+多少數(shù)值)和onClickFunction(點擊事件)
// Button.js
import React from 'react'
const Button = ({ increment, onClickFunction }) => {
const handleClick = () => {
onClickFunction(increment)
}
return <button onClick={handleClick}>+{increment}</button>
}
export default 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>
<div>learn react</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
接下來,我們將用 @testing-library/react幫助我們測試按鈕點擊后的結(jié)果是不是正常。
首先讓我們測試Button組件。在Button組件中創(chuàng)建一個Button.test.js文件。
//Button.test.js
import React from 'react'
import { render, fireEvent } from '@testing-library/react'
import Button from './Button'
我們從react-testing-library引入render和fireEvent工具方法,render能夠幫助我們渲染JSX,fireEvent能讓我們在組件上觸發(fā)一個事件。
由于Button組件通過接收一個方法綁定到click事件上,我們添加一個count變量,同時創(chuàng)建一個增加它的方法
//Button.test.js
let count
const incrementCount = increment => {
count += increment
}
現(xiàn)在,我們開始寫一個測試用例。首先我們初始化一個count值為0,然后在我們Button組件上increment傳值為1,onClickFunction傳我們定義的方法incrementCount,緊接著我們?nèi)カ@取組件第一個子元素的值,檢查它是不是顯示+1。
我們通過代碼去觸發(fā)點擊事件,查看count值是不是從0變成了1。
//Button.test.js
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的按鈕點擊之后是不是也功能正常:
//Button.test.js
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個按鈕組件,我們可以測試每個按鈕在點擊事件之后,是不是加上了對應(yīng)的數(shù)值,我們可以盡可能多的測試幾次。
// App.test.js
import React from 'react'
import {render, screen, fireEvent} from '@testing-library/react'
import App from './App'
test('renders learn react link', () => {
render(<App />)
const linkElement = screen.getByText(/learn react/i)
expect(linkElement).toBeInTheDocument()
})
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')
})
我們就可以看到下面的測試結(jié)果:

結(jié)語
好了,到此為止,react組件測試流程就完整的跑過了一遍。在真實的開發(fā)場景中,功能和邏輯會更復(fù)雜,本篇文章的目的在于拋磚引玉,如果對自動化測試感興趣的同學(xué),可以參考jest和@testing-library/react官方文檔,在自己的項目中應(yīng)用上去。