1. 程式人生 > 實用技巧 >在 Angular 中引入 Jest 進行單元測試

在 Angular 中引入 Jest 進行單元測試

用 Karma 在專案中遇到了坑

最近新換了一個專案,去的時候專案已經做了兩個月了,因為前期趕功能,沒有對單元測試做要求,CI/CD 的時候也沒有強制跑單元測試。所以雖然有用 Angular CLI 自動生成的測試檔案,但是基本上都是測試不通過。
專案做久了,人員變動多,新來的成員對之前的業務邏輯不清不楚,稍不注意就會破壞之前的功能;業務複雜了,隨便增加或者修改一點點功能都可能引起不易被察覺的 BUG。作為一個敬業的開發,不上單元測試怎麼行。所以,就有了一個修復已有單元測試的任務。
修復已有測試檔案的思路很簡單:寫個 TestingModule 把常用的依賴 mock 掉,再引入到需要的檔案中就行了;不常用的依賴,在各自的檔案中 mock 掉就好了。
然而實際操作起來的時候,Karma 早早挖好坑等這了。有些測試檔案單跑沒有問題,整體跑得時候就報錯,測試結果及其不穩定;karma 的報錯資訊又特別難讀懂,很多時候根本定位不到到底是哪裡出了問題。再加上 Karma 需要先把 Angular 應用編譯之後再在瀏覽器中跑測試,整體時間也比較慢,修復的過程一直處於抓狂的邊緣。
整體測試跑起來的時候難以定位測試出錯的定位,怎麼辦呢,那就讓跑整個測試的時候各個檔案之間也沒有依賴可以單獨跑好了,所以就想到了 Jest。實踐證明,在 Angular 中, Jest 大法也非常好使。

Karma 和 Jest 的對比

前面也說過了,在修復測試的過程中,karma 遇到了各種各樣的問題。歸結起來大概就是:

  • Karma 需要先把 Angular 應用整體編譯之後再在瀏覽器中跑測試,跑測試的時間比較長;
  • Karma 測試結果不穩定(很可能是因為非同步操作引起的),單個檔案和整體測試時的測試結果不一致;
  • 報錯資訊模糊不清,無法定位問題。特別是在有大量測試需要修復的情況下,難以定位問題的根本原因。

那麼對比而言,Jest 在上面這些方面都有很好的表現:

  • 不需要整體編譯,可以單檔案測試
  • 測試結果穩定
  • 報錯清楚,易於定位問題

除了這些,Jest 還有的好處有:

  • 開箱即用,基本算是全家桶,包含了測試需要的大部分工具:測試結構、斷言、spies、mocks
  • 直接提供了測試覆蓋率報告
  • 快照測試
  • 非常強大的模組級 mock 功能
  • watch 模式僅僅測試和被修改檔案相關的測試,速度非常快

遷移

第一步,你需要相關依賴包:

npminstall--save-dev jest jest-preset-angular @types/jest

其中:

  • jest – Jest 測試框架
  • jest-preset-angular – jest 對於 angular 的一些通用的預設定
  • @types/jest – Jest 的 typings

第二步,你需要在 package.json 中對 Jest 進行配置:

"jest": {
  "preset": "jest-preset-angular",
  "setupFilesAfterEnv": ["<rootDir>/src/setupJest.ts"]
}

其中,preset聲明瞭預設,setupFilesAfterEnv配置了 Jest setup 檔案的地址,可以包含多個檔案,這裡設定的是專案根目錄下的src/setupJest.ts。

第三步,在 src 目錄下建立上一步中設定的 setup 檔案setupJest.ts

import 'jest-preset-angular'; // jest 對於 angular 的預配置
import './jestGlobalMocks'; // jest 全域性的 mock

第四步,在 src 目錄下建立jestGlobalMocks.ts檔案,並加入相關的全域性的 mock,以下是一個例子:

const mock = () => {
  let storage = {};
  return {
    getItem: key => key in storage ? storage[key] : null,
    setItem: (key, value) => storage[key] = value || '',
    removeItem: key => delete storage[key],
    clear: () => storage = {},
  };
};

Object.defineProperty(window, 'localStorage', {value: mock()});
Object.defineProperty(window, 'sessionStorage', {value: mock()});
Object.defineProperty(window, 'getComputedStyle', {
  value: () => ['-webkit-appearance']
});

可以看到這個例子中 mock 了 window 上的物件,這是因為jsdom 並沒有實現所有的 window 上的物件和方法,所以有時我們需要自己給 window 打個補丁。
在這裡 mocklocalStorage是可選的,如果我們在程式碼中並沒有使用。但是 mockgetComputedStyle是必須的,因為 Angular 會檢查它在哪個瀏覽器中執行。如果沒有 mockgetComputedStyle,我們的測試程式碼將無法執行。

接下來,我們就可以在 package.json 的 script 中配置 test 的命令了:

"test": "jest",
"test:watch": "jest --watch",

其中test只跑一次測試,test:watch可以檢測檔案變化,跑當前有修改的檔案的相關測試。

此時,在命令列中執行測試命令,就應該能夠順利把測試跑起來並通過了。如果沒有通過,可能是因為我們在src/tsconfig.spec.json中的 file 配置中有test.js的配置,這是 Karma 的 setup 檔案,刪掉這行配置並刪除對應的檔案,(src/tsconfig.app.json中出現的test.js也可一併刪除),重新跑一遍測試命令:

npm runtest

至此,Jest 測試環境就算順利搭建好了。如果你對程式碼有潔癖,接下來,你還可以刪除 Karma 的相關程式碼,將測試全部轉為 Jest。

廣州品牌設計公司https://www.houdianzi.com PPT模板下載大全https://redbox.wode007.com

刪除 Karma 相關程式碼

刪除相關依賴包(@types/jasmine @types/jasminewd2 jasmine-core jasmine-spec-reporter 因為在 e2e 測試中有使用所以不能刪除):

npm uninstall karma karma-chrome-launcher karma-coverage-istanbul-reporter karma-jasmine karma-jasmine-html-reporter

刪除檔案src/karma.config.js

刪除 angular.json 中 test 的配置

src/tsconfig.spec.json中compilerOptions.type的配置移除 jasmine, 加上 jest。

至此,你已經刪除了所有與 Karma 相關的程式碼。你甚至還能將測試斷言換成 jest 的風格。
檢視最後生成的程式碼庫和相關檔案配置