寫(xiě)在前面
使用 react hook 來(lái)做公司的新項(xiàng)目有一段時(shí)間了,大大小小的坑踩了不少。由于是公司項(xiàng)目,因此必須要編寫(xiě)單元測(cè)試來(lái)確保業(yè)務(wù)邏輯的正確性以及重構(gòu)時(shí)代碼的可維護(hù)性與穩(wěn)定性,之前的項(xiàng)目使用的是 react@15.x 的版本,使用 enzyme 配合 jest 來(lái)做單元測(cè)試毫無(wú)壓力,但新項(xiàng)目使用的是 react@16.8 ,編寫(xiě)單元測(cè)試的時(shí)候,遇到不少阻礙,因此總結(jié)此篇文章算作心得分享出來(lái)。
配合 enzyme 來(lái)進(jìn)行測(cè)試
首先,enzyme 對(duì)于 hook 的支持程度,可以參考這個(gè) issue,對(duì)于各個(gè) hook 的支持程度,里面有鏈接,有說(shuō)明,這里就不贅述了。我在這里想說(shuō)的是,使用 enzyme 來(lái)測(cè)試 hook 在測(cè)試以及驗(yàn)證方式上的一些轉(zhuǎn)變。
測(cè)試狀態(tài)
由于 function component 沒(méi)有實(shí)例的概念,我們無(wú)法通過(guò)類(lèi)似 instance.xxx 的方式來(lái)直接對(duì)狀態(tài)進(jìn)行驗(yàn)證,比如:
對(duì)于這里的 count 是無(wú)法通過(guò) enzyme 中 wrapper.state 的 api 來(lái)訪問(wèn)的,但是我們可以通過(guò) wrapper.text 來(lái)取出 button 的文字節(jié)點(diǎn),間接地測(cè)試 count 狀態(tài),如:
const Counter = () => { const [count, setCount] = useState(0) return <button>{count}</button>}測(cè)試方法
同理,我們也無(wú)法通過(guò) instance.methodXXX 的方式來(lái)直接獲取組件實(shí)例的方法,進(jìn)而進(jìn)行調(diào)用和測(cè)試,比如:
const wrapper = mount(<Counter/>)expect(wrapper.find('button').text()).toBe('0')如何獲取 inc 方法的引用呢?我們可以通過(guò) wrapper.prop 來(lái)曲線救國(guó):
const Counter = () => { const [count, setCount] = useState(0) const inc = useCallback(() => setCount(c => c + 1), []) return <button onClick={inc}>{count}</button>}另外,有些情況下,我們以返回值的方式來(lái)暴露 hook 中的一些狀態(tài)以及方法,如果是這樣的話,就更簡(jiǎn)單了,可以通過(guò)編寫(xiě) Wrapper 組件或者直接使用下一小節(jié)提及的工具庫(kù)來(lái)進(jìn)行測(cè)試。
使用 @testing-library/react-hooks
測(cè)試有返回值的 hook
關(guān)于這個(gè)工具庫(kù),在它的代碼倉(cāng)庫(kù)中的 README.md 對(duì)它要解決的問(wèn)題、實(shí)現(xiàn)原理進(jìn)行了詳細(xì)的說(shuō)明,有興趣的甚至可以直接看它的源碼,十分簡(jiǎn)單。這里給出一個(gè)示例來(lái)演示如何測(cè)試上一小節(jié)最后所說(shuō)的情況,比如我們有一個(gè) 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 }}首先,我們完全可以通過(guò)上一小節(jié)的方式來(lái)對(duì)它進(jìn)行測(cè)試,只需要實(shí)現(xiàn)一個(gè)臨時(shí)的 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>}
新聞熱點(diǎn)
疑難解答
圖片精選