React要寫單元測試愤钾?不妨這樣試試

長期以來触创,單元測試 (Unit testing/UT) 都是前端項目工程化繞不開的一個重點坎藐。而近兩年隨著越來越多更為便利的測試框架、工具的出現(xiàn),端到端測試(End-to-end testing/E2E)在項目實踐中的存在感也越來越強(qiáng)岩馍,面對E2E的“蠶食”碉咆,我們不得不思考,編寫UT最合理的“度”在哪里蛀恩?另一方面疫铜,過去一年多,React Hooks強(qiáng)勢崛起双谆,大家的編碼習(xí)慣在潛移默化中發(fā)生了不小的改變壳咕,與之相對,UT的實踐策略也應(yīng)不斷進(jìn)行調(diào)整顽馋,如何在React項目中落地單元測試谓厘,是一個很值得深入的話題。

對于Java趣避、C#等后端語言庞呕,UT的實踐策略已經(jīng)比較成熟新翎,而在前端領(lǐng)域程帕,UT始終處于一個復(fù)雜且細(xì)分的階段。翻閱社區(qū)的眾多資料地啰,UT在不同項目中的實踐方式可謂是五花八門愁拭,大家嘗試套用后端語言的成熟經(jīng)驗,然而在落地時亏吝,又會遇到很多水土不服的問題岭埠,讓執(zhí)行變得異常艱難。這篇文章蔚鸥,正是想要提供一種思路惜论,以解決問題為導(dǎo)向,嘗試找到一個初步的實踐策略止喷。

import

論述UT重要性或是講解如何實踐測試驅(qū)動開發(fā)(Test-Driven Development/TDD)的文章已經(jīng)很多[1]馆类,此處不再贅述,本文主要針對以下幾點問題進(jìn)行討論:

  1. React組件化后弹谁,組件哪部分最具測試價值乾巧?
  2. 如何讓我們的測試用例更易編寫、維護(hù)预愤?
  3. UT與E2E的邊界在哪里沟于?

帶著這些問題,我們從React組件本身出發(fā)植康,一探究竟旷太。

一、React組件哪部分最具測試價值销睁?

1. Component

Component 應(yīng)著重關(guān)注render以及副作用供璧,同時業(yè)務(wù)邏輯的處理過程标沪,都應(yīng)該盡量提取到Hooks和Utils文件中。因此嗜傅,對于Component的測試金句,我們完全可以將重心主要放在以下這兩方面問題上:

  • 組件是否正常渲染了?
  • 組件副作用是否正常處理了吕嘀?

在嘗試使用UT對這兩個關(guān)注點進(jìn)行覆蓋時违寞,首先就要面臨一個比較棘手的問題:開發(fā)人員需要mock整個組件渲染所需的所有數(shù)據(jù),包括且不限于Redux store中的state和所有的Props偶房,如此才可保證組件能夠被正確渲染且覆蓋符合預(yù)期趁曼。而這就意味著開發(fā)人員在編寫測試用例時不得不耗費(fèi)很大一部分精力mock數(shù)據(jù),且在開發(fā)后期棕洋,極有可能為了頁面新增的一個字段挡闰,開發(fā)人員卻需要花費(fèi)不小的工時對mock數(shù)據(jù)進(jìn)行維護(hù)。

反觀E2E掰盘,由于其并不關(guān)注程序的內(nèi)部實現(xiàn)摄悯,因而在覆蓋并解決上述兩方面問題的同時,相對輕松地避開了UT所遭遇的痛點愧捕,故此我們更傾向于將基礎(chǔ)組件渲染這部分內(nèi)容的測試工作移交給E2E來負(fù)責(zé)奢驯,UT只需要關(guān)注帶有復(fù)雜顯示邏輯的組件。

同理次绘,如果你的組件內(nèi)部包含復(fù)雜的渲染邏輯瘪阁,你依然可以使用UT對其進(jìn)行覆蓋,我們推薦使用react-testing-library來加載組件邮偎,mock接口之后管跺,再寫一個類似E2E的集成測試。當(dāng)然使用Enzyme直接進(jìn)行render的測試也是個不錯的方案禾进。

2. Hooks

如何測試React Hooks豁跑,社區(qū)目前已有相對成熟的解決方案,即@testing-library/react-hooks + react-test-renderer[2]命迈。通過這兩個依賴贩绕,開發(fā)人員可以很輕松的mock出Hooks執(zhí)行所依賴的環(huán)境,把store的數(shù)據(jù)當(dāng)作hooks的輸入壶愤,關(guān)注在hooks內(nèi)的業(yè)務(wù)邏輯淑倾,即可把Hooks當(dāng)作純方法(Pure Function)來進(jìn)行測試。

3. Redux/Slice

對于Redux征椒,如果項目在使用 Redux Toolkit 的話娇哆,事情會簡單很多,開發(fā)人員只需要關(guān)注Dispatch的Actions即可。但如果Actions和Reducer是分開編寫碍讨,則需要針對性處理:

  • Action

對于Action creator治力,雖然官網(wǎng)展示了對應(yīng)的測試用例形式,但是大多數(shù)情況勃黍,這一部分都是類似的模版代碼:

const orderLoading = () => ({ type: 'ORDER_LOADING' });

針對這類代碼鋪測試用例宵统,唯一的效果只會是增加開發(fā)人員復(fù)制粘貼的工作量。這部分測試用例真正需要關(guān)注的覆获,應(yīng)是dispatch的那一部分代碼邏輯:我們對actions的dispatch是否符合預(yù)期马澈?對Service返回數(shù)據(jù)的處理是否符合預(yù)期?諸如此類弄息。

export const getOrderById = (orderId: string): AppThunk => async (dispatch) => {
  try {
    dispatch(orderLoading);
    const orders = await requestOrderAPI([mockOrder]);
    dispatch(addOrders(orders));
  } catch (error) {
    dispatch(orderLoadingError(error));
  }
};

  • Reducer

由于所有對于store state的操作痊班,都應(yīng)該放在action中來完成,因而大多數(shù)情況下摹量,Reducer都是模版代碼涤伐。確實對于這類純函數(shù),編寫測試用例會輕松很多缨称,但就實際情況而言凝果,大部分的這類模板代碼都沒有測試的必要。

當(dāng)然具钥,如果reducer中還包含了對state的邏輯處理豆村,甚至于涉及業(yè)務(wù)的分支邏輯,UT覆蓋還是很有價值的骂删。

4. Redux Selectors

不同應(yīng)用場景中,Selectors的復(fù)雜程度可高可低四啰。若Selectors只是簡單且直接地返回store中存儲的某項數(shù)據(jù)時宁玫,不需要UT覆蓋;然而若涉及數(shù)據(jù)聚合柑晒、清洗等邏輯操作時欧瘪,UT覆蓋不能偷懶。

5. Service

不同項目或團(tuán)隊對Service的定義各不相同匙赞,這里我們要聊的主要指負(fù)責(zé)處理HTTP請求的request和response佛掖,以及相應(yīng)的異常處理的數(shù)據(jù)層。Service主要的功能是對接Action涌庭,因而理想情況下Service只需要包含與API通信的代碼芥被,這種情況下,UT可有可無坐榆。但一些場景下拴魄,如果項目中沒有使用BFF承擔(dān)數(shù)據(jù)處理的角色,后端也沒能提供完全符合前端數(shù)據(jù)結(jié)構(gòu)需求的接口時,不可避免的匹中,開發(fā)人員需要在此處完善數(shù)據(jù)處理的邏輯夏漱,以便獲取清洗或聚合后的數(shù)據(jù),因而這種情況下顶捷,UT覆蓋是非常有必要的挂绰。

6. Utils/Helpers

Utils/Helpers主要包含以下幾類類型:

  • 數(shù)據(jù)結(jié)構(gòu)的轉(zhuǎn)化,各種convert工具函數(shù)
  • 數(shù)據(jù)結(jié)構(gòu)的處理服赎,比如數(shù)據(jù)提取扮授、合并壓縮、整理工具函數(shù)
  • 公共的工具函數(shù)

根據(jù)我們目前的項目習(xí)慣专肪,當(dāng)一段邏輯需要在Utils/Helpers中實現(xiàn)時刹勃,那么它一定是純函數(shù),其中多數(shù)情況又會包含一定程度的數(shù)據(jù)處理邏輯嚎尤,所以基本都需要UT覆蓋荔仁。

二、如何讓我們的測試用例更易編寫芽死、維護(hù)乏梁?

回答這個問題,我們需要先思考一下关贵,什么樣的測試用例編寫起來最輕松遇骑?答案可能因人而異,但輸入輸出簡單明了的純函數(shù)一定能算上一個揖曾。從這個觀點出發(fā)落萎,結(jié)合黑盒測試的特性,我們可以將這個問題拆分為以下兩點:

1. 如何讓輸入輸出更清晰炭剪?

這個問題练链,說到底是管理mock數(shù)據(jù)的問題。隨著項目的不斷膨脹奴拦,組織mock數(shù)據(jù)會逐漸成為編寫UT時負(fù)擔(dān)最重的那個環(huán)節(jié)媒鼓。隨手mock在項目前期可能會稍顯方便,但這無異于給自己挖坑错妖。

最直接的解決方案還是首選集中管理mock數(shù)據(jù):項目中可以考慮集中維護(hù)一個DTO mock集合绿鸣,其中提供不同類型的Base DTO mock數(shù)據(jù),由各個測試用例在使用時按需導(dǎo)入暂氯,再在其內(nèi)部轉(zhuǎn)化成他所需要的數(shù)據(jù)潮模,具體實現(xiàn)方式可因項目而異,在搭建出框架后株旷,通過使用的方式來進(jìn)一步明確項目中的需求再登,進(jìn)行調(diào)整尔邓。

2. 如何讓過程更簡單?

要回答這個問題锉矢,既“簡單”又“困難”梯嗽,因為答案的核心很明確,即降低代碼的深度和復(fù)雜度沽损,控制代碼分支數(shù)量灯节,如此這般在一定程度上減少測試用例。但在實際場景中绵估,無論是編碼水平有限炎疆,項目框架限制還是需求時限要求,總有各種各樣“合理”的理由阻礙開發(fā)人員將代碼寫得簡單国裳。這種情況下形入,不妨多了解一些關(guān)于TDD的實踐方法,在避免形式主義的前提下缝左,結(jié)合項目情況亿遂,嘗試改變一些既定的編碼習(xí)慣。同時渺杉,有舍有得蛇数,根據(jù)F.I.R.S.T.原則[3],對已有UT測試用例進(jìn)行優(yōu)化和重構(gòu)是越。

三耳舅、UT與E2E的邊界在哪里?

在實踐E2E的過程中倚评,我們意識到為了提高E2E的可維護(hù)性及測試用例的運(yùn)行效率浦徊,E2E的關(guān)注點應(yīng)更側(cè)重于從更高的維度,對于項目整體的流水線進(jìn)行測試蔓纠,而非過分關(guān)注具體的細(xì)節(jié)辑畦,如某一個按鈕的顯隱。

且隨著E2E測試用例數(shù)量的增加腿倚,在維護(hù)的過程中,只有不斷進(jìn)行精簡與合并蚯妇,逐漸刪減掉那些過于獨(dú)立的測試用例敷燎,并將不同環(huán)節(jié)的獨(dú)立測試用例串聯(lián)為完整的流程,如此才能保證E2E的健壯箩言。

因此硬贯,顯而易見的,UT與E2E在編寫或維護(hù)過程中陨收,確實存在重疊的可能性饭豹,但它們最終形態(tài)的關(guān)注點卻是完全不同的鸵赖,而關(guān)注點的差異,正是其邊界所在拄衰。

export

最后它褪,為 TL;DR 的同學(xué)簡單總結(jié)一下:

  • UT應(yīng)關(guān)注代碼中最具測試價值的部分,以盡可能小的成本換取最大化的收益
  • 測試價值取決于項目本身的側(cè)重點及開發(fā)人員的編碼習(xí)慣翘悉,這里提供一種思路供參考:
    • Component:應(yīng)覆蓋包含復(fù)雜顯示邏輯的組件茫打,除此之外可以不覆蓋
    • Hooks: 應(yīng)全部覆蓋
    • Redux:應(yīng)覆蓋action函數(shù),及包含數(shù)據(jù)處理邏輯的reducer函數(shù)妖混,除此之外可以不覆蓋
    • Selectors:應(yīng)覆蓋包含數(shù)據(jù)處理邏輯的函數(shù)老赤,除此之外可以不覆蓋
    • Service:應(yīng)覆蓋包含數(shù)據(jù)處理邏輯的函數(shù),除此之外可以不覆蓋
    • Utils/Helpers:應(yīng)全部覆蓋
  • 確定關(guān)注點制市,同時通過對測試用例不斷的分解和組合抬旺,在實踐中明確UT及E2E的邊界

參考資料:

  1. React單元測試策略及落地

  2. testing-library/react-hooks-testing-library

  3. Agile in a Flash - F.I.R.S.T

“卓派前端工作志,聚焦實用前端技術(shù)祥楣,讓編程更有趣开财!”

前端技術(shù)組 @ 西安卓派科技 NEXT Trucking — 拉勾 | Boss | 知乎 | 掘金 | 簡書

如果覺得本文對你有幫助的話,快來關(guān)注我們吧荣堰!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末床未,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子振坚,更是在濱河造成了極大的恐慌薇搁,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件渡八,死亡現(xiàn)場離奇詭異啃洋,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)屎鳍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門宏娄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人逮壁,你說我怎么就攤上這事孵坚。” “怎么了窥淆?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵卖宠,是天一觀的道長。 經(jīng)常有香客問我忧饭,道長扛伍,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任词裤,我火速辦了婚禮刺洒,結(jié)果婚禮上鳖宾,老公的妹妹穿的比我還像新娘。我一直安慰自己逆航,他們只是感情好鼎文,可當(dāng)我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著纸泡,像睡著了一般漂问。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上女揭,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天蚤假,我揣著相機(jī)與錄音,去河邊找鬼吧兔。 笑死磷仰,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的境蔼。 我是一名探鬼主播灶平,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼箍土!你這毒婦竟也來了逢享?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤吴藻,失蹤者是張志新(化名)和其女友劉穎瞒爬,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體沟堡,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡侧但,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了航罗。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片禀横。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖粥血,靈堂內(nèi)的尸體忽然破棺而出柏锄,到底是詐尸還是另有隱情,我是刑警寧澤复亏,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布绢彤,位于F島的核電站,受9級特大地震影響蜓耻,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜械巡,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一刹淌、第九天 我趴在偏房一處隱蔽的房頂上張望饶氏。 院中可真熱鬧,春花似錦有勾、人聲如沸疹启。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽喊崖。三九已至,卻和暖如春雇逞,著一層夾襖步出監(jiān)牢的瞬間荤懂,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工塘砸, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留节仿,地道東北人。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓掉蔬,卻偏偏與公主長得像廊宪,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子女轿,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 在這里說一下前端開發(fā)的一個特點是更多的會涉及用戶界面箭启,當(dāng)開發(fā)規(guī)模達(dá)到一定程度時,幾乎注定了其復(fù)雜度會成倍的增長蛉迹。 ...
    愛碼小士閱讀 5,884評論 1 4
  • 寫在前面 關(guān)于MVP關(guān)于MVP的介紹很多傅寡,這不是本文的重點,這里列舉近期一些比較好的文章婿禽。 Android官方MV...
    geniusmart閱讀 32,878評論 23 349
  • TL;DR——什么是好的單元測試赏僧? 其實我是個標(biāo)題黨,單元測試根本沒有“藝術(shù)”可言扭倾。 好的測試來自于好的代碼淀零,如果...
    ThoughtWorks閱讀 3,140評論 1 22
  • 本文為實踐的產(chǎn)物,總結(jié)從搭建到使用過程中遇到的問題膛壹,初學(xué)者可以參考驾中。 Jest 和 Enzyme 的基本介紹 測試...
    寧小姐的慢時光閱讀 4,617評論 0 4
  • 久違的晴天,家長會模聋。 家長大會開好到教室時肩民,離放學(xué)已經(jīng)沒多少時間了。班主任說已經(jīng)安排了三個家長分享經(jīng)驗链方。 放學(xué)鈴聲...
    飄雪兒5閱讀 7,493評論 16 22