怎么對reacthooks進(jìn)行單元測試-創(chuàng)新互聯(lián)

這篇文章主要介紹怎么對react hooks進(jìn)行單元測試,文中介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們一定要看完!

創(chuàng)新互聯(lián)公司專業(yè)為企業(yè)提供臨澤網(wǎng)站建設(shè)、臨澤做網(wǎng)站、臨澤網(wǎng)站設(shè)計、臨澤網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計與制作、臨澤企業(yè)網(wǎng)站模板建站服務(wù),十年臨澤做網(wǎng)站經(jīng)驗,不只是建網(wǎng)站,更提供有價值的思路和整體網(wǎng)絡(luò)服務(wù)。

寫在前面

使用 react hook 來做公司的新項目有一段時間了,大大小小的坑踩了不少。由于是公司項目,因此必須要編寫單元測試來確保業(yè)務(wù)邏輯的正確性以及重構(gòu)時代碼的可維護(hù)性與穩(wěn)定性,之前的項目使用的是 react@15.x 的版本,使用 enzyme 配合 jest 來做單元測試毫無壓力,但新項目使用的是 react@16.8 ,編寫單元測試的時候,遇到不少阻礙,因此總結(jié)此篇文章算作心得分享出來。

配合 enzyme 來進(jìn)行測試

首先,enzyme 對于 hook 的支持程度,可以參考這個 issue,對于各個 hook 的支持程度,里面有鏈接,有說明,這里就不贅述了。我在這里想說的是,使用 enzyme 來測試 hook 在測試以及驗證方式上的一些轉(zhuǎn)變。

測試狀態(tài)

由于 function component 沒有實例的概念,我們無法通過類似 instance.xxx 的方式來直接對狀態(tài)進(jìn)行驗證,比如:
對于這里的 count 是無法通過 enzyme 中 wrapper.state 的 api 來訪問的,但是我們可以通過 wrapper.text 來取出 button 的文字節(jié)點,間接地測試 count 狀態(tài),如:

const Counter = () => {
 const [count, setCount] = useState(0)
 return <button>{count}</button>
}

測試方法

同理,我們也無法通過 instance.methodXXX 的方式來直接獲取組件實例的方法,進(jìn)而進(jìn)行調(diào)用和測試,比如:

const wrapper = mount(<Counter/>)
expect(wrapper.find('button').text()).toBe('0')

如何獲取 inc 方法的引用呢?我們可以通過 wrapper.prop 來曲線救國:

const Counter = () => {
 const [count, setCount] = useState(0)
 const inc = useCallback(() => setCount(c => c + 1), [])
 return <button onClick={inc}>{count}</button>
}

另外,有些情況下,我們以返回值的方式來暴露 hook 中的一些狀態(tài)以及方法,如果是這樣的話,就更簡單了,可以通過編寫 Wrapper 組件或者直接使用下一小節(jié)提及的工具庫來進(jìn)行測試。

使用 @testing-library/react-hooks

測試有返回值的 hook

關(guān)于這個工具庫,在它的代碼倉庫中的 README.md 對它要解決的問題、實現(xiàn)原理進(jìn)行了詳細(xì)的說明,有興趣的甚至可以直接看它的源碼,十分簡單。這里給出一個示例來演示如何測試上一小節(jié)最后所說的情況,比如我們有一個 hook:

function useCounter() {
 const [count, setCount] = useState(0)
 const inc = useCallback(() => setCount(c => c + 1), [])
 const dec = useCallback(() => setCount(c => c - 1), [])
 
 return {
  count,
  inc,
  dec
 }
}

首先,我們完全可以通過上一小節(jié)的方式來對它進(jìn)行測試,只需要實現(xiàn)一個臨時的 Wrapper,比如:

const CounterIncWrapper = () => {
 const {count, inc} = useCounter()
 return <button onClick={inc}>{count}</button>
}

const CounterDecWrapper = () => {
 const {count, dec} = useCounter()
 return <button onClick={dec}>{count}</button>
}

然后單獨按照上一節(jié)提及的方式來測試 CounterIncWrapper 或者 CounterDecWrapper 就可以了,但我們會發(fā)現(xiàn),這里的 Wrapper 的邏輯是很相似的,我們是否可以將它抽離為一個公用的邏輯呢?答案當(dāng)然是可以的,這正是 @testing-library/react-hooks 做的,使用它我們可以這樣測試 hook ,如下:

test('should increment counter', () => {
 const { result } = renderHook(() => useCounter())

 act(() => {
  result.current.inc()
 })

 expect(result.current.count).toBe(1)
 
 act(() => {
  result.current.dec()
 })

 expect(result.current.count).toBe(0)
})

這里的 act 是內(nèi)置的工具方法,可以參考官方文檔進(jìn)行了解,任何對于狀態(tài)的修改,都應(yīng)該在它的回調(diào)函數(shù)中進(jìn)行,不然會出現(xiàn)錯誤警告。

測試有依賴項的 hook

有些情況下,我們的 hook 會存在依賴的,比較常見的是 useContext 這個 hook ,它依賴一個 Provider 父組件,比如輕量級的狀態(tài)管理庫 unstated-next ,假設(shè)我們將上面的 hook 抽象成了一個獨立的 Container (這里會涉及 unstated-next 的 api ,但不影響理解):

const Counter = createContainer(useCounter)

要使用這個 Container ,我們需要這樣:

可以發(fā)現(xiàn),這里的 CounterDisplay 依賴于 Counter.Provider ,要測試 CounterDisplay ,我們通過 renderHook 的 wrapper 參數(shù)來注入父組件,比如:

function CounterDisplay() {
 let counter = Counter.useContainer()
 return (
  <div>
   <button onClick={counter.dec}>-</button>
   <span>{counter.count}</span>
   <button onClick={counter.inc}>+</button>
  </div>
 )
}

function App() {
 return (
  <Counter.Provider>
   <CounterDisplay />
  </Counter.Provider>
 )
}

另外, renderHook 還支持 initialProps 參數(shù),它代表回調(diào)函數(shù)中的參數(shù),這里接不贅述了。

測試副作用

hook 中比較難搞的應(yīng)該算是 useEffect ,我花了很長時間來看別人是如何對它進(jìn)行單元測試的,但是并沒有得到一些有用的信息,后來我仔細(xì)想了想,其實這個問題應(yīng)該這樣來想, useEffect 是用來封裝副作用的,它只用來負(fù)責(zé)副作用的運行時機(jī),對于副作用干了什么,對于 useEffect 完全是透明的。因此我們沒有必要對它進(jìn)行單元測試,而應(yīng)該在副作用的實現(xiàn)層確保它的正確性。但我們通常會將副作用的實現(xiàn)與 hook 的實現(xiàn)耦合起來,那怎么對副作用的實現(xiàn)進(jìn)行測試呢?這里可以分兩種情況。

useEffect 會運行 props 中傳遞的回調(diào)函數(shù)

這種情況相對簡單一些,只需要通過 jest.fn() 來構(gòu)造一個 spy 函數(shù),之后通過上一節(jié)的方式渲染 hook ,通過 jest 對于 spy 函數(shù)的 api 來進(jìn)行驗證即可。

useEffect 自成一體

這種情況下,我當(dāng)前是通過將副作用代碼,直接聲明在 hook 外部的方式來進(jìn)行測試的,比如:

export function updateDocumentTitle(title) {
 document.title = title
 
 return () => {
   document.title = 'default title'
 }
}

export function useDocumentTitle(title) {
 useEffect(() => updateDocumentTitle(title), [title])
}

這樣,只需要單獨測試 updateDocumentTitle 就好,而不需要在 useEffect 上花費功夫了。

這里可能有的人會問,你這里無法覆蓋 title 改變時, effect 是否重新運行的場景,確實,當(dāng)前我也沒有辦法解決這種問題,如果要解決,辦法還是有的,就是通過 useDocumentTitle 的參數(shù),來傳遞 updateDocumentTitle ,但這對于代碼有很強的侵入性,我不建議這樣做,如果 hook 本身的實現(xiàn)方式就是這樣,那完全可以針對它編寫相關(guān)的測試用例,如果不是,也沒有必要為了寫測試用例而改寫原來的實現(xiàn)。

hook 無法被測試的原因

在對公司項目各個 hook 編寫單元測試時,發(fā)現(xiàn)一些 hook 非常難以測試,大體的特征如下:

  • hook 的實現(xiàn)非常復(fù)雜,狀態(tài)繁多,依賴繁多

  • hook 的實現(xiàn)不復(fù)雜,但外部依賴難以 mock

  • hook 的實現(xiàn)自成一體,沒有入口

關(guān)于第一點,解決的方法當(dāng)然是,化繁為簡,將復(fù)雜的 hook,劃分為多個簡單的 hook,使其職責(zé)更單一。對于第二點,如果外部依賴難以 mock ,我建議將它的測試用例放到集成測試階段進(jìn)行實現(xiàn),而不要花費過多精力在編寫單元測試的 mock 邏輯上。

以上是“怎么對react hooks進(jìn)行單元測試”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對大家有幫助,更多相關(guān)知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!

網(wǎng)頁題目:怎么對reacthooks進(jìn)行單元測試-創(chuàng)新互聯(lián)
分享鏈接:http://muchs.cn/article12/dshodc.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供手機(jī)網(wǎng)站建設(shè)、服務(wù)器托管、網(wǎng)站內(nèi)鏈關(guān)鍵詞優(yōu)化、軟件開發(fā)、外貿(mào)網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

h5響應(yīng)式網(wǎng)站建設(shè)