Web 前端單元測試到底要怎麼寫?
隨著 Web 應用的複雜程度越來越高,很多公司越來越重視前端單元測試。我們看到的大多數教程都會講單元測試的重要性、一些有代表性的測試框架 api 怎麼使用,但在實際項目中單元測試要怎麼下手?測試用例應該包含哪些具體內容呢?
本文從一個真實的應用場景出發,從設計模式、代碼結構來分析單元測試應該包含哪些內容,具體測試用例怎麼寫,希望看到的童鞋都能有所收穫。
項目用到的技術框架
該項目採用 技術棧,用到的主要框架包括: 、 、 、 、 、 、 、 。
應用場景介紹
這個應用場景從 UI 層來講主要由兩個部分組成:
工具欄,包含刷新按鈕、關鍵字搜索框
表格展示,採用分頁的形式瀏覽
看到這裡有的童鞋可能會說:切!這麼簡單的界面和業務邏輯,還是真實場景嗎,還需要寫神馬單元測試嗎?
別急,為了保證文章的閱讀體驗和長度適中,能講清楚問題的簡潔場景就是好場景不是嗎?慢慢往下看。
設計模式與結構分析
在這個場景設計開發中,我們嚴格遵守 單向數據流 與 的最佳實踐,並採用 來處理業務流, 來處理狀態緩存,通過 來調用後台介面,與真實的項目沒有差異。
分層設計與代碼組織如下所示:
中間 中的內容都是 相關的,看名稱應該都能知道意思了。
具體的代碼請看 這裡。
單元測試部分介紹
先講一下用到了哪些測試框架和工具,主要內容包括:
,測試框架
,專測 react ui 層
,具有獨立的 fakes、spies、stubs、mocks 功能庫
,模擬 HTTP Server
如果有童鞋對上面這些使用和配置不熟的話,直接看官方文檔吧,比任何教程都寫的好。
接下來,我們就開始編寫具體的測試用例代碼了,下面會針對每個層面給出代碼片段和解析。那麼我們先從 開始吧。
為使文章盡量簡短、清晰,下面的代碼片段不是每個文件的完整內容,完整內容在這裡。
actions
業務裡面我使用了 來產生 ,這裡用工具欄做示例,先看一段業務代碼:
對於 測試,我們主要是驗證產生的 對象是否正確:
這個測試用例的邏輯很簡單,首先構建一個我們期望的結果,然後調用業務代碼,最後驗證業務代碼的運行結果與期望是否一致。這就是寫測試用例的基本套路。
我們在寫測試用例時盡量保持用例的單一職責,不要覆蓋太多不同的業務範圍。測試用例數量可以有很多個,但每個都不應該很複雜。
reducers
接著是 ,依然採用 的 來編寫 ,這裡用表格的來做示例:
這裡的狀態對象使用了
對於 ,我們主要測試兩個方面:
對於未知的 ,是否能返回當前狀態。
對於每個業務 type ,是否都返回了經過正確處理的狀態。
下面是針對以上兩點的測試代碼:
這裡的測試用例邏輯也很簡單,依然是上面斷言期望結果的套路。下面是 selectors 的部分。
selectors
的作用是獲取對應業務的狀態,這裡使用了 來做緩存,防止 未改變的情況下重新計算,先看一下表格的 selector 代碼:
這裡的分頁器部分參數在項目中是統一設置,所以 reselect 很好的完成了這個工作:如果業務狀態不變,直接返回上次的緩存。分頁器默認設置如下:
那麼我們的測試也主要是兩個方面:
對於業務 selector ,是否返回了正確的內容。
緩存功能是否正常。
測試代碼如下:
測試用例依然很簡單有木有?保持這個節奏就對了。下面來講下稍微有點複雜的地方,sagas 部分。
sagas
這裡我用了 處理業務流,這裡具體也就是非同步調用 api 請求數據,處理成功結果和錯誤結果等。
可能有的童鞋覺得搞這麼複雜幹嘛,非同步請求用個 不就完事了嗎?別急,耐心看完你就明白了。
這裡有必要大概介紹下 的工作方式。saga 是一種 的生成器函數 - Generator ,我們利用他來產生各種聲明式的 ,由 引擎來消化處理,推動業務進行。
這裡我們來看看獲取表格數據的業務代碼:
不熟悉 的童鞋也不要太在意代碼的具體寫法,看注釋應該能了解這個業務的具體步驟:
從對應的 里取到調用 api 時需要的參數部分(搜索關鍵字、分頁),這裡調用了剛才的 selector。
組合好參數並調用對應的 api 層。
如果正常返回結果,則發送成功 action 通知 reducer 更新狀態。
如果錯誤返回,則發送錯誤 action 通知 reducer。
那麼具體的測試用例應該怎麼寫呢?我們都知道這種業務代碼涉及到了 api 或其他層的調用,如果要寫單元測試必須做一些 mock 之類來防止真正調用 api 層,下面我們來看一下 怎麼針對這個 saga 來寫測試用例:
這個測試用例相比前面的複雜了一些,我們先來說下測試 saga 的原理。前面說過 saga 實際上是返回各種聲明式的 ,然後由引擎來真正執行。所以我們測試的目的就是要看 的產生是否符合預期。那麼 到底是個神馬東西呢?其實就是字面量對象!
我們可以用在業務代碼同樣的方式來產生這些字面量對象,對於字面量對象的斷言就非常簡單了,並且沒有直接調用 api 層,就用不著做 mock 咯!這個測試用例的步驟就是利用生成器函數一步步的產生下一個 ,然後斷言比較。
從上面的注釋 3、4 可以看到, 還提供了一些輔助函數來方便的處理分支斷點。
這也是我選擇 的原因:強大並且利於測試。
api 和 fetch 工具庫
接下來就是api 層相關的了。前面講過調用後台請求是用的 ,我封裝了兩個方法來簡化調用和結果處理: 、 ,分別對應 GET 、POST 請求。先來看看 api 層代碼:
業務代碼很簡單,那麼測試用例也很簡單:
由於 api 層直接調用了工具庫,所以這裡用 來替換工具庫達到測試目的。
接著就是測試自己封裝的 fetch 工具庫了,這裡 fetch 我是用的 ,所以選擇了 來模擬 Server 進行測試,主要是測試正常訪問返回結果和模擬伺服器異常等,示例片段如下:
基本也沒什麼複雜的,主要注意 fetch 是 promise 返回, 的各種非同步測試方案都能很好滿足。
剩下的部分就是跟 UI 相關的了。
容器組件
容器組件的主要目的是傳遞 state 和 actions,看下工具欄的容器組件代碼:
那麼測試用例的目的也是檢查這些,這裡使用了 來模擬 redux 的 store :
很簡單有木有,所以也沒啥可說的了。
UI 組件
這裡以表格組件作為示例,我們將直接來看測試用例是怎麼寫。一般來說 UI 組件我們主要測試以下幾個方面:
是否渲染了正確的 DOM 結構
樣式是否正確
業務邏輯觸發是否正確
下面是測試用例代碼:
得益於設計分層的合理性,我們很容易利用構造 來達到測試目的,結合 和 ,測試用例依然保持簡單的節奏。
總結
以上就是這個場景完整的測試用例編寫思路和示例代碼,文中提及的思路方法也完全可以用在 、 項目上。完整的代碼內容在 這裡 (重要的事情多說幾遍,各位童鞋覺得好幫忙去給個 哈)。
最後我們可以利用覆蓋率來看下用例的覆蓋程度是否足夠(一般來說不用刻意追求 100%,根據實際情況來定):
單元測試是 TDD 測試驅動開發的基礎。從以上整個過程可以看出,好的設計分層是很容易編寫測試用例的,單元測試不單單只是為了保證代碼質量:他會逼著你思考代碼設計的合理性,拒絕麵條代碼
借用 Clean Code 的結束語:
2005 年,在參加于丹佛舉行的敏捷大會時,Elisabeth Hedrickson 遞給我一條類似 Lance Armstrong 熱銷的那種綠色腕帶。這條腕帶上面寫著「沉迷測試」(Test Obsessed)的字樣。我高興地戴上,並自豪地一直系著。自從 1999 年從 Kent Beck 那兒學到 TDD 以來,我的確迷上了測試驅動開發。
不過跟著就發生了些奇事。我發現自己無法取下腕帶。不僅是因為腕帶很緊,而且那也是條精神上的緊箍咒。那腕帶就是我職業道德的宣告,也是我承諾盡己所能寫出最好代碼的提示。取下它,彷彿就是違背了這些宣告和承諾似的。
所以它還在我的手腕上。在寫代碼時,我用餘光瞟見它。它一直提醒我,我做了寫出整潔代碼的承諾。
作者:deepfunc
https://segmentfault.com/a/1190000015935519
※大廠 H5 開發實戰手冊
※Hi,你的10G大數據免費學習資料包到了,請簽收!
TAG:JavaScript |