新聞中心
今天看了 Kent 博客的 《How to know what to test》 的這篇博客,感覺醍醐灌頂,今天就把這篇博文也分享給大家。

創(chuàng)新互聯(lián)公司公司2013年成立,先為芒市等服務(wù)建站,芒市等地企業(yè),進行企業(yè)商務(wù)咨詢服務(wù)。為芒市企業(yè)網(wǎng)站制作PC+手機+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問題。
正片開始
知道如何做測試很好,也很重要的。我之前就教過很多人測試的基礎(chǔ)知識、如何配置工具、如何針對不用情況寫好測試,等等。但是知道如何測試只是成功的一半,知道要測什么才是更重要的另一半。
永遠(yuǎn)記住為什么我們要測試
我們寫測試是因為要確保我們的應(yīng)用程序在用戶使用它們時能夠正常工作。 有些人可能會用測試用例來提高工作流的效率,但我對提高代碼信心更有興趣,即:我們的測試應(yīng)該能直接增強我們的代碼信心。這也是我希望你在編寫測試時要考慮的重點:
別太糾結(jié)于正在測試的代碼,而要多考慮這些代碼能夠支持的真實用例。
如果你只考慮代碼本身,很容易、也很自然地走向測試代碼細(xì)節(jié)的不歸路。我們應(yīng)該要考慮那些更接近用戶的真實使用場景來寫測試。
Code Coverage < Use Case Coverage
在做測試時,代碼覆蓋率是表示我們的代碼有多行被執(zhí)行的一個指標(biāo)。舉下面這個例子:
function arrayify(maybeArray) {
if (Array.isArray(maybeArray)) {
return maybeArray
} else if (!maybeArray) {
return []
} else {
return [maybeArray]
}
}現(xiàn)在,我們還沒有給這個函數(shù)寫測試,所以這個函數(shù)的覆蓋率為 0%。這種情況下的代碼覆蓋率報告可以讓我們知道:得馬上寫測試了,但它沒有告訴我們這個函數(shù)有哪些重要的部分,也沒有告訴我們這個函數(shù)支持的真實用例(正是我們在寫測試時最要重點關(guān)注的內(nèi)容)是哪些。
實際上,當(dāng)我們在考慮應(yīng)該對整個應(yīng)用中哪些部分做測試時,覆蓋率報告對于 “我們應(yīng)該在哪部分投入更多時間” 這個問題幫助不是很大。
覆蓋率報告只能幫助我們知道哪些代碼還沒納入測試。所以,當(dāng)你看著這份覆蓋率報告時,你不要總想著那些 if/else、循環(huán)或者生命周期,而是要問問自己:
這幾行代碼實現(xiàn)對應(yīng)的是哪些使用用例?我應(yīng)該要加哪些測試用例來覆蓋它們?
“使用用例覆蓋率” 可以告訴我們當(dāng)前測試支持了哪些使用用例??上У氖牵F(xiàn)在并沒有類似 “使用用例覆蓋率報告” 這么一說。我們只能自己實現(xiàn)。不過,代碼覆蓋率報告有時候也能告訴我們哪些使用用例沒有覆蓋到。
舉上面函數(shù)為例子,看到它的第一眼,我們就能馬上想到它的第一個真實用例:“傳入數(shù)組則返回數(shù)組”。這就可以作為我們測試用例的標(biāo)題了:
test('傳入數(shù)組則返回數(shù)組', () => { expect(arrayify(['Elephant', 'Giraffe'])).toEqual(['Elephant', 'Giraffe'])})
有了上面的測試用例,我們的覆蓋情況如下所示(高亮部分為覆蓋部分):
現(xiàn)在,讓我們來看看還沒被覆蓋的那部分,然后發(fā)現(xiàn)還有兩種 Use Case 還沒支持:
- 傳入 falsy 值,則返回空數(shù)組
- 傳入非 falsy 值且不是數(shù)組時,返回一個數(shù)組,其中包含的輸入值
現(xiàn)在再來把測試用例都加上,然后再來看覆蓋情況:
test('傳入 falsy 值,則返回空數(shù)組', () => {
expect(arrayify()).toEqual([])
})馬上就可以覆蓋完了!
test(`傳入非 falsy 值也不是數(shù)組時,返回一個數(shù)組,其中包含的輸入值`, () => { expect(arrayify('Leopard')).toEqual(['Leopard'])})
好了,現(xiàn)在只要我們保證不改變這個函數(shù)的這些使用方法,那么我們有信心地說:這些測試都是能通過的。
代碼覆蓋率并不是一個完美的指標(biāo),但它卻能幫助我們制作自己的 “使用用例覆蓋率”。
代碼覆蓋率也能隱藏使用用例
有的時候,代碼覆蓋率是 100%,但不意味著使用用例也被覆蓋了 100%。這就是為什么我有時候在寫測試前都會把所有的使用用例想清楚。
舉個例子,假設(shè)有一個 arrayify 函數(shù):
test(`傳入非 falsy 值也不是數(shù)組時,返回一個數(shù)組,其中包含的輸入值`, () => {
expect(arrayify('Leopard')).toEqual(['Leopard'])
})我們用這兩個用例來實現(xiàn) 100% 的代碼覆蓋:
- 輸入數(shù)組,返回數(shù)組
- 輸入非數(shù)組,返回數(shù)組,其中包含輸入內(nèi)容
如果我們來思考一下真實的使用用例,會發(fā)現(xiàn)少了一種 Case:
- 輸入 Falsy 值,返回空數(shù)組
如果用戶直接用 arrayify(),那么這樣的測試用例就不能很好地給足我們代碼的信心了。雖然現(xiàn)在看起來還行,就算不給這個 Case 寫測試,我們的代碼也支持這樣的用例,但是,之所以我們要寫測試,是因為我們要確保做了代碼變更之后,它都能支持我們想要的使用用例。
我們繼續(xù)來看這樣做測試的后果:假如現(xiàn)在有人看到這一行 filter(Boolean) ,然后覺得:這是哪個 SB 想到的奇葩寫法。最終把這里去掉了。然而,我們的測試依舊是可以通過的,但所有依賴 “輸入 falsy 值” 的這個 Case 的代碼就都掛了。
要對使用用例做測試,而不是代碼
如何應(yīng)用到 React 代碼的測試?
在寫測試時,你應(yīng)該時刻想著要支持兩種用戶:真實用戶和開發(fā)者。 再啰嗦一句,如果做測試的時候,你還是一直想著業(yè)務(wù)代碼而不是真實用例,就會很容易陷入測試 “代碼實現(xiàn)細(xì)節(jié)” 的陷阱。而這么做的后果是,你的代碼會無形中創(chuàng)造第三種用戶:Test User。
很多人在做 React 代碼測試時,經(jīng)常會想到一些讓他們不斷測 “實現(xiàn)細(xì)節(jié)” 的測試點。對此,應(yīng)該別把太多注意點放在要測試的業(yè)務(wù)代碼上,多想想那些會對真實用戶以及開發(fā)者產(chǎn)生影響的東西是什么,這才是你應(yīng)該要思考的 Use Case,比如:
- 生命周期方法
- 元素事件回調(diào)
- 組件內(nèi)部狀態(tài)
相反,一些跟上面兩類用戶有關(guān)的一些東西也是要做測試的,比如,它們都會改變 DOM、發(fā) HTTP 請求、執(zhí)行 Prop 里的回調(diào),或者產(chǎn)生一些可觀察到的副作用,把它們拿來做測試是很有幫助的:
- 用戶交互(使用 @testing-library/user-event 里的 userEvent):用戶是否在和渲染出來的組件進行交互?
- 修改 Prop(使用 React Testing Library 里的 rerender):如果別的開發(fā)者用新的 Props 來渲染你的組件呢?
- 修改 Context(使用 React Testing Library 里的 rerender):如果別的開發(fā)者修改了 Context 導(dǎo)致你的組件重新渲染呢?
- 修改訂閱:如果組件訂閱的事件中心做了修改呢?(比如:firebase、redux store、router、media query)
該從何測起?
現(xiàn)在我們都清楚應(yīng)該要對單測組件或者頁面組件測什么了,那你該從何測起呢?這確實是個讓人頭大的問題,尤其是你要對一個巨大無比的應(yīng)用進行測試的時候。
好,現(xiàn)在這是你要做的事:從真實用戶的角度來看以及問:
如果應(yīng)用崩了,那么哪部分會讓人最不爽?
或者換個問法:
應(yīng)用崩了,最糟糕的地方在哪里?
我會建議你按這個標(biāo)準(zhǔn)來列出你應(yīng)用支持功能的優(yōu)先級。 你可以和你的團隊以及 Leader 一起來做這件事,這將會是一次很好的嘗試。而且這次嘗試也會有很多好處:幫助所有人搞清楚測試的重要性,并說服他們:測試也是一件優(yōu)先級很高的事情。
一旦有了這份優(yōu)先級清單,我會建議你寫一個端對端的測試來覆蓋住用戶使用最多的場景。一般來說,這種方法都能覆蓋住這份清單前幾項功能。你可能需要多的時間來做這個測試,但是一切都是值得的。
雖然這個 E2E 測試不會給你 100% 的 Use Case 覆蓋率(你千萬別嘗試去弄),也不會給你 100% 代碼覆蓋率(你甚至都別想著要記錄 E2E 的覆蓋率),但它會給你一個很好的開始,而且能立即增強你對當(dāng)前代碼的信心。
一旦有了幾個 E2E 測試用例之后,你就可以給一些沒在 E2E 范圍內(nèi)的邊界情況做集成測試,然后再給用到的功能里更復(fù)雜的業(yè)務(wù)邏輯做單元測試。從現(xiàn)在開始,剩下的事情就是不斷加測試就好了。只是別老想著要 100% 的覆蓋率了,不值當(dāng)。
總結(jié)
如果有足夠的時間和經(jīng)驗,你會培養(yǎng)出一種知道要測試什么的直覺。你可能會犯錯誤或者難受,不要放棄!穩(wěn)住,我們能贏。
好了,這篇外文就給大家?guī)У竭@里了??偟膩碚f,也是很贊同 “要多關(guān)注 Use Case 的覆蓋情況而不是代碼覆蓋情況”,畢竟如果完全按照代碼覆蓋率這個指標(biāo)來的話,有太多的作弊手段了,這完全和寫測試的初衷是相違背的。寫測試的目的應(yīng)該是增強我們對代碼的自信心,而不是功利地看某個指標(biāo)。
后面 Kent 說到要如何把測試引入團隊的方法也很值得大家去嘗試:先按功能優(yōu)先級列出個清單,再寫 E2E 覆蓋住最重要的那部分,再加集成測試,再加單元測試,等一切就緒,那么剩下的就是時間堆測試用例,最后測試用例也能慢慢融入到代碼中了。
當(dāng)前標(biāo)題:前端單測,我們應(yīng)該測什么?
地址分享:http://www.dlmjj.cn/article/ccoshod.html


咨詢
建站咨詢
