前端單元測試入門

Gerryli發表於2021-08-25

簡介

日常我們都忙於寫bug,有時候不妨停下腳步,找找bug。測試廣義上分為黑盒測試和白盒測試。單元測試屬於後者,是在知道程式執行邏輯的基礎上,設計測試用例,確保程式模組行為與我們預期一致。

前端做單元測試的不多,但是並不代表不必要,對於一些複雜的資料處理、公共類庫等,單元測試是非常有必要的,程式碼只要有修改,就是有風險的,檢驗程式碼正確與否,最安全的方法就是通過設計的所有測試用例。通過本文,你將會收穫以下內容:

  1. Node.js中如何做單元測試,如何生成測試報告與測試覆蓋率
  2. vue.js單檔案元件如何從零開始搭建單元測試環境

Node.js單元測試

巧婦難為無米之炊,在做單元測試前,我們需要藉助一款測試框架與一個斷言庫,測試框架是輔助我們編寫測試用例,而斷言主要是用來判斷預期輸出與實際輸出是否一致。本文測試框架選用mocha,斷言採用expect.js

CommonJS與ES6模組化區別

現在前端開發環境幾乎都執行在Node.js中,只是最後打包成靜態資原始檔。所以單純只想針對單純專案中JavaScript檔案進行單元測試,會因為模組化的差異而執行失敗,因為ES6中使用的是import/export,而CommonJS中使用的是require/module.exports,想了解Node.js 如何處理 ES6 模組,可以參考阮老師文章。本文推薦一個第三方類庫esm,僅需兩步就能實現Node.js載入ES6 module。使用流程如下:

  • 安裝
npm install esm -D
  • 使用
const $require = require('esm')(module)
// 注意:如果是export default,匯入的模組需要通過.default獲取
const TestModule = $require('ES6Module.js')

測試程式碼

以下為測試程式碼,主要測試加法運算及函式柯里化處理過程是否正確。

class Calc {
  constructor() {
    this.add = this.addFun()
  }
  // 函式柯里化
  addFun () {
    let args = []
    let calcFun = function (...params) {
      args = args.concat(params)
      return args.reduce((total, current) => {
        return total + current
      }, 0)
    }

    calcFun.clear = function () {
      args = []
    }

    return calcFun
  }
}

export default = Calc

編寫測試用例

測試框架採用mocha,一般編寫單元測試用例,離不開describeitdescribe類似於開發中的class,是一類測試用例的集合;it相當於普通function,是一個具體測試用例實現。如下為簡單的測試用例,簡單羅列了普通測試與非同步測試的基本使用,更多使用方法可以參考mocha官網

const Calc = require('../class/calc.js')
const expect = require('expect.js')

const calcInstance = new Calc()
describe('Test calc add operation', function () {
  it('should 1 + 2 === 3', function () {
    expect(calcInstance.add(1, 2)).to.be(3)
  });

  it('should summary + 2 === 4', function () {
    expect(calcInstance.add(2)).to.be(5)
  });

  it('should 1 + 2 === 3 after clear', function () {
    // 指定當前測試用例超時時間
    this.timeout(0)
    calcInstance.add.clear()
    // 非同步測試
    return new Promise(resolve => {
      setTimeout(() => {
        resolve()
      }, 2000)
    }).then(() => {
      expect(calcInstance.add(1, 2)).to.be(3)
    })
  });
})

單元測試指標

覆蓋率簡述

覆蓋率是單元測試核心指標,又細分為語句覆蓋率(Statements)、分支覆蓋率(Branches)、函式覆蓋率(Functions)、行覆蓋率(Lines)。一個比較好的單元測試用例,要保證被測試程式碼的關鍵邏輯都被測試用例覆蓋到。如下為Calc類的單元測試覆蓋率情況。

單元測試覆蓋率統計

開源元件nyc可以幫助我們統計覆蓋率情況。

  • 安裝
npm install nyc -D
  • 配置檔案
// nyc.config.js
module.exports = {
  all: true,
  include: [ // 包含測試檔案
    "web/src/lib/**.js",
    ".electron/class/**.js",
    "web/src/**/*.js",
    "web/src/**/*.vue"
  ],
  exclude: [ // 排除測試檔案
    "web/test/*.spec.js",
    ".electron/test/*.spec.js"
  ],
  checkCoverage: false, // 測試基準覆蓋率
  reporter: ['text', 'html'] // 測試報告格式
}
  • 使用
// package.json
"test-add": "nyc mocha .electron/test/calc.spec.js"

視覺化單元測試報告

mocha官方推薦使用mochawesome用於生成視覺化測試報告,使用非常簡單。

  • 安裝
npm install mochawesome -D
  • 使用
// package.json
"test-add-reporter": "mocha .electron/test/calc.spec.js --reporter mochawesome --reporter-options reportDir=mochaReporter,reportFilename=addReporter"

注意事項

編寫單元測試用例時,不僅要考慮測試覆蓋率,還要考慮測試用例設計。合格的測試用例不僅要包含正常情況輸入輸出,也要包含異常情況輸入輸出的判斷,不能以開發的思維去編寫測試用例。

vue元件的單元測試

vue單檔案元件測試官方腳手架也有整合,但是還是要抱著試一試的態度,去學習下如何從零開始搭建一個vue單檔案元件測試環境。

.vue檔案並不能直接執行,需要對其進行打包編譯,轉換成瀏覽器能識別的靜態資原始檔才行。目前有開源的工具mochapack可以提供這種能力。見名知意,普通mocha測試用例是可以直接執行的,但是遇到非javascript語法,就會執行報錯。mochapack主要功能是在測試用例執行前利用webpack對測試用例進行編譯轉換,這樣就不用擔心執行出錯。具體配置如下:

// 基礎配置
{
  "test-vue-sfc": "mochapack  --webpack-config ./build/webpack.test.config.js --require ./web/test/setup.js web/test/demo.spec.js"
}
  • --webpack-config 指定webpack配置檔案
  • --require 指定環境依賴狀態,如nodejs中不存在dom,需要引入js-dom
  • web/test/demo.spec.js 需要執行的測試用例

vue測試用例編寫

測試用例是用nodejs執行,並沒有瀏覽器環境,所以我們藉助官方提供的測試工具@vue/test-utils對元件進行模擬掛載、點選等。使用方法也比較簡單,以下為簡單的測試用例。

import Vue from 'vue'
import ElementUI from 'element-ui'
import TestDemo from '../src/components/test-demo.vue'
import { shallowMount } from '@vue/test-utils'

Vue.use(ElementUI)

describe('Test test-demo.vue', function () {
  this.timeout(0)
  const wrapper = shallowMount(TestDemo)
  wrapper.vm.count = 1
  it('should summary === 1', function () {
    expect(wrapper.vm.summary).to.be(1)
  });
})

結語

對於通用性、處理邏輯複雜等模組單元,做單元測試收益會很大的,至少每次改動的對錯,都能有一個較明顯的反饋。單元測試不僅要注意一定程度的覆蓋率,也要注意測試用例設計的合理性。完整程式碼可以參考electron-demo,這個專案是electron開發demo,裡面是本人日常開發中常見的坑點,有興趣的可以關注下。

相關文章