Jest unit test

在angular中利用Jest進行測試之解析

江志卿 2019/12/23 10:06:06
1387

前言

有些人會程式碼寫一寫後,沒再寫單元測試,編譯後沒問題,直接就交給QC測試,測試中出了問題再回頭改。但在某個專案中,突然遇到客戶的要求:需要進行單元測試,並且以SonarQube進行程式碼檢查以及覆蓋率至少要達到80%以上。剛收到需求時頓時間腦子裡充滿問號,從沒接觸過測試,不知道從何開始。測試是要測試甚麼?甚麼是覆蓋率?

接下來說一下測試的流程,我們會在專案中安裝測試的工具,寫好單元測試碼,在本地端先進行程式碼的測試,如果本地端跑過沒有錯誤訊息,在命令列上會出現目前的覆蓋率(覆蓋率簡單來說就是你的單元測試碼佔整個專案中應該測試而已經測試的比率。)。此時我們再把code push上git server,再以CI/CD工具讓SonarQube去跑一份測試報告。SonarQube是分析程式碼品質的工具,它以七個指標來分析程式碼品質:程式架構面、多餘程式碼、單元測試、複雜度、潛在問題、編碼規則以及註解。所以在SonarQube中能清楚看到各項指標評比後的分數,以及建議改善的地方。下圖為在SonarQube中觀看某專案的儀錶板畫面。

在本文中,我不介紹SonarQube的安裝,僅介紹在本地端,如何做到利用Jest進行單元測試,得到測試報告。Jest是由Facebook所開發的一套測試框架,功能相當齊全,它具有以下幾個特色:

  • 強大的模仿功能(內建mock function,能模擬程式碼的方法並回傳mock值)。
  • 內置斷言庫。
  • 內置的程式碼覆蓋率生成器。
  • 以JSDOM模擬瀏覽器的DOM,讓使用者在運行test case時,無須任何額外的設定。

 

安裝設定

1、利用angular-cli建立一新專案。

ng new my-app

2、在專案中安裝所需套件

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

3、在專案的根目錄下建立jest.config.js,jest.config.js為Jest的設定檔,它會在我們專案中src層的資料夾中尋找.spec.ts檔案,並且在運行測試之前,會先在記憶體中編譯專案的Typescript程式碼。

const { pathsToModuleNameMapper } = require('ts-jest/utils');
const { compilerOptions } = require('./tsconfig');

module.exports = {
  preset: 'jest-preset-angular',
  roots: ['<rootDir>/src/'],
  testMatch: ['**/+(*.)+(spec).+(ts)'],
  setupFilesAfterEnv: ['<rootDir>/src/test.ts'],
  collectCoverage: true,
  coverageReporters: ['lcov','text-summary'],
  coverageDirectory: 'coverage/my-app',
  moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths || {}, {
    prefix: '<rootDir>/'
  })
};

4、更新src/test.ts檔案內容。主要用途為引入jest-preset-angular模組以設置angular測試環境;以及模擬全域windows物件的一些屬性及功能,以確保測試可以在JSDOM環境中運行。

import 'jest-preset-angular';

Object.defineProperty(window, 'CSS', {value: null});
Object.defineProperty(window, 'getComputedStyle', {
  value: () => {
    return {
      display: 'none',
      appearance: ['-webkit-appearance']
    };
  }
});

Object.defineProperty(document, 'doctype', {
  value: '<!DOCTYPE html>'
});
Object.defineProperty(document.body.style, 'transform', {
  value: () => {
    return {
      enumerable: true,
      configurable: true
    };
  }
});

5、更改tsconfig.spec.json的內容。

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "outDir": "./out-tsc/spec",
    "types": [
      "jest",
      "node"
    ],
    "esModuleInterop": true,
    "emitDecoratorMetadata": true
  },
  "files": [
    "src/test.ts",
    "src/polyfills.ts"
  ],
  "include": [
    "src/**/*.spec.ts",
    "src/**/*.d.ts"
  ]
}

6、執行npx jest,即可獲得運行結果。

運行後,在專案根目錄底下,會產生一資料夾coverage\my-app\lcov-report,點擊index.html用瀏覽器開啟,也可以看到更detail的覆蓋率報告,並可按照提示訊息進行測試碼修改,非常好用。

 

解析報告

測試碼就好像爬蟲一樣,一旦開始測試時會去爬你的原始碼,爬到的範圍越多越廣,覆蓋率的分數就越高。所以在寫測試程式時,如果遇到應覆蓋而未覆蓋的情況時,在思路上應去思考為何測試碼爬不過去,可能是受測的變數為空,或是測試碼在生命週期上與原始碼有出入。

覆蓋率報告中,有分成四種覆蓋率Statements、Branches、Functions、Lines,代表的涵義是:

  • Statements : 專案中受測的檔案內,每一段程式碼的陳述句被測試碼執行到的比率。(甚麼是陳述句?)
  • Branches : 原始碼中所有具有分支(if else...)的部分,是否都有被測試碼執行到的比率。
  • Functions : 原始碼中所有的方法(method),是否都有被測試碼執行到的比率。
  • Lines : 原始碼中每一行程式碼,是否都有被測試碼執行到的比率。

在測試報告中,提供了一些tag,提示使用者在哪些地方,測試碼還尚未"覆蓋"到。

'I'代表if的判斷式以及陳述句尚未被測試碼執行到。以上範例中if else一邊未被執行到,Branches覆蓋率僅剩50%。

'E'代表else的判斷式以及陳述句尚未被測試碼執行到。同樣的,if else一邊未被執行到,Branches覆蓋率僅剩50%。

'nx'綠色的數字代表被執行的次數。

未執行的行或代碼段將以紅色區塊突出顯示;未執行的陳述句是以粉紅色顯示;未執行的方法是以橙色顯示;未執行的分支是以黃色顯示。

因此,測試覆蓋率能夠幫助我們了解我們的測試案例的有效性,我們是否哪邊遺漏了測試,進而提升程式碼品質。

江志卿