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

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

爲何要從 Karma 遷移到 Jest

用 Karma 在項目中遇到了坑

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

Karma 和 Jest 的對比

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

  • Karma 須要先把 Angular 應用總體編譯以後再在瀏覽器中跑測試,跑測試的時間比較長;
  • Karma 測試結果不穩定(極可能是由於異步操做引發的),單個文件和總體測試時的測試結果不一致;
  • 報錯信息模糊不清,沒法定位問題。特別是在有大量測試須要修復的狀況下,難以定位問題的根本緣由。

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

  • 不須要總體編譯,能夠單文件測試
  • 測試結果穩定
  • 報錯清楚,易於定位問題

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

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

遷移

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

npm install --save-dev jest jest-preset-angular @types/jest

其中:npm

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

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

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

其中,preset 聲明瞭預設,setupFilesAfterEnv 配置了 Jest setup 文件的地址,能夠包含多個文件,這裏設置的是項目根目錄下的 src/setupJest.ts瀏覽器

第三步,在 src 目錄下建立上一步中設置的 setup 文件 setupJest.tssession

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

第四步,在 src 目錄下建立 jestGlobalMocks.ts 文件,並加入相關的全局的 mock,如下是一個例子:app

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 打個補丁。
在這裏 mock localStorage 是可選的,若是咱們在代碼中並無使用。可是 mock getComputedStyle 是必須的,由於 Angular 會檢查它在哪一個瀏覽器中執行。若是沒有 mock getComputedStyle,咱們的測試代碼將沒法執行。

接下來,咱們就能夠在 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 run test

至此,Jest 測試環境就算順利搭建好了。若是你對代碼有潔癖,接下來,你還能夠刪除 Karma 的相關代碼,將測試所有轉爲 Jest。

刪除 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.jsoncompilerOptions.type 的配置移除 jasmine, 加上 jest。

至此,你已經刪除了全部與 Karma 相關的代碼。你甚至還能將測試斷言換成 jest 的風格。
查看最後生成的代碼庫和相關文件配置


參考:

相關文章
相關標籤/搜索