前端單測的那些事

muwoo發表於2018-08-20

前言

因為經常需要維護一些大型的業務專案和一些自己的開源專案,所以為了更好的“規範”程式碼質量和迭代的穩定性,開始寫了一些單測。下面也主要是自己的一些總結吧,由於測試工具和框架很多,這裡只介紹一些browser端常用的測試工具,文中如果有問題也歡迎拍正!!?

為何要測試

之前我們開發專案的時候,總是會忽略去寫一寫單測,大多數原因可能是覺得沒有時間或者是浪費時間。而且還需要去維護測試用例。 其實一般專案隨著時間的遷移,會變得越來越複雜和龐大,這時候如果我們對某一個公共模組改動,而其他人也依賴了這個模組,可能就會導致別人的功能出現bug。 有了自動化測試,開發者會更加信任自己的程式碼。開發者再也不會懼怕將程式碼交給別人維護,不用擔心別的開發者在程式碼裡搞“破壞”。後人接手一段有測試用例的程式碼,修改起來也會更加從容。測試用例裡非常清楚的闡釋了開發者和使用者對於這端程式碼的期望和要求,也非常有利於程式碼的傳承。

一些概念

TDD (Test Driven Development)

TDD 也就是測試驅動開發,簡單的說,即在寫任何功能程式碼之前,先寫它的測試程式碼。具體步驟:

  • 根據需要編寫一個測試用例
  • 編寫功能程式碼,以讓剛才的測試用例通過
  • 逐步補充測試用例
  • 修改功能程式碼使新增的測試用例和原來的都通過
  • 重構,包括功能程式碼和測試用例

BDD(Behaviour Driven Development)

BDD 即 行為驅動開發,可以理解為也是 TDD 的分支,即也是測試驅動,但 BDD 強調的是寫測試的風格,即測試要寫得像自然語言,運用一些比如expect、should等跟自然語言相近的斷言,讓專案的各個成員甚至產品都能看懂測試,甚至編寫測試。

寫法對比

不管是 TDD 還是 BDD,我們來對比一些二者的寫法:

  // TDD
  suite('Array', function() {
    setup(function() {
    });

    test('equal -1 when index beyond array length', function() {
      assert.equal(-1, [1,2,3].indexOf(4));
    });
  });

  // BDD
  describe('Array', function() {
    before(function() {
    });

    it('should return -1 when no such index', function() {
      [1,2,3].indexOf(4).should.equal(-1);
    });
  });
複製程式碼

簡單的看, BDD 風格寫的會更容易理解,更加語義化。

斷言(assert)

在單元測試時,開發預計在程式執行到某個節點位置,需要判斷某些邏輯條件必須滿足,這樣下面的一些業務邏輯才可以進行下去,如果不滿足,程式就會"報錯"甚至是"崩潰"。斷言在單測中,也是主要用來確定某段程式執行結果應該是某個值這樣的一個預期。

一些斷言庫的比較

斷言庫即提供一套 API 幫助開發者在單元測試的過程中判定某個值是否符合預期,比如:

should.js
  value.should.equal(1);
  value.should.be.an.Object();
複製程式碼
expect.js
expect(value).to.be(1)
expect(value).to.be.an('object')
複製程式碼

chai

Chai is a BDD / TDD assertion library for node and the browser that can be delightfully paired with any javascript testing framework.

chai 提供了三種斷言風格來分別適用於 BDD 和 TDD。expect/should API 對應BDD風格,Assert API 對應TDD風格。

image

測試框架

所謂"測試框架",就是執行測試的工具。通過它,可以為JavaScript應用新增測試,從而保證程式碼的質量。

Mocha

Mocha(發音"摩卡")誕生於2011年,是現在最流行的JavaScript測試框架之一,在瀏覽器和Node環境都可以使用。 更多關於mocha的使用和介紹可以參考阮老師的這篇文章測試框架 Mocha 例項教程

Jasmine

Jasmine 不依賴於任何框架,所以適用於所有的 Javascript 程式碼。使用一個全域性函式 describe 來描述每個測試,並且可以巢狀。describe函式有2個引數,一個是字串用於描述,一個是函式用於測試。在該函式中可以使用全域性函式it 來定義Specs,也就是單元測試的主要內容, 使用 expect 函式來測試:

describe("A suite is just a function", function() {
  var a;
  it("and so is a spec", function() {
    a = true;
    expect(a).toBe(true);
  });
});
複製程式碼

前端測試工具

相比於服務端開發,前端開發在測試方面始終面臨著一個嚴峻的問題,那就是瀏覽器相容性。前端開發中瀏覽器相容性是一個永遠的問題,而且我認為即使解決了瀏覽器的相容性問題,未來在移動開發方面,裝置相容性也是一個問題。

所以在自動化測試方面也是如此,即使所有的單元測試集中在了一個runner中,前端測試仍然要面對至少4個瀏覽器核心以及3個電腦作業系統加2個或更多移動作業系統,何況還有令移動開發人員頭疼的Android的碎片化問題。不過可以安心的是,早已存在這樣的工具可以捕獲不同裝置上的不同瀏覽器,並使之隨時更新測試結果,甚至可以在一個終端上看到所有結果。下面我們主要介紹 Karma

Karma

Karma是一個基於 Node.js 的 JavaScript 測試執行過程管理工具(Test Runner)。該工具可用於測試所有主流Web瀏覽器,也可整合到 CI(Continuous integration)工具,也可和其他程式碼編輯器一起使用。這個測試工具的一個強大特性就是,它可以監控(Watch)檔案的變化,然後自行執行,通過console.log顯示測試結果。

文件

Istanbul

測試的時候,我們常常關心,是否所有程式碼都測試到了。 這個指標就叫做"程式碼覆蓋率"(code coverage)。它有四個測量維度。

  • 行覆蓋率(line coverage):是否每一行都執行了?
  • 函式覆蓋率(function coverage):是否每個函式都呼叫了?
  • 分支覆蓋率(branch coverage):是否每個if程式碼塊都執行了?
  • 語句覆蓋率(statement coverage):是否每個語句都執行了?

這個軟體以土耳其最大城市伊斯坦布林命名,因為土耳其地毯世界聞名,而地毯是用來覆蓋的。

詳細的文件參考阮老師的程式碼覆蓋率工具 Istanbul 入門教程

動手!

下面我們來搭建一個ES6 + jasmine + karma + Istanbul + webpack 的基礎測試工具。 首先來搭建一個簡單的es6環境:

mkdir example

cd example

npm i babel-core bable-loader babel-preset-env --save
複製程式碼

然後建立.babelrc檔案

{
    "presets": [
        "env"
    ]
}
複製程式碼

接著我們需要安裝 karma 測試工具:

$ npm install karma --save-dev

$ npm install karma-jasmine karma-chrome-launcher jasmine-core --save-dev
複製程式碼

接下來初始化karma:

karma init
複製程式碼

image

這樣目錄中便會多了一個karma.conf.js這樣的一個檔案。為了在測試的時候可以使用es6,我們需要使用karma-webpack

npm i webpack karma-webpack --save
複製程式碼

接下來,就是我們熟悉的工作了,寫一個載入測試指令碼的webpack配置檔案,比如webpack.test.js:

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        }
      }
    ]
  }
};
複製程式碼

再改寫一下我們的karma.conf.js, 引入webpack:

const webpack = require('../webpack.config')

module.exports = function(config) {
  config.set({
    basePath: '',
    frameworks: ['jasmine'],
    files: [
      'test/**/*.spec.js'
    ],
    exclude: [
    ],
    preprocessors: {
      'test/add.spec.js': ['webpack']
    },
    webpack: webpack,
    reporters: ['progress'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['Chrome'],
    singleRun: false,
    concurrency: Infinity
  })
}
複製程式碼

執行karma start

image

當前我們的reporters設定的是progress,所以只會展示最終測試結果,和執行錯誤的語句。如果你想看到每一條的執行結果,可以安裝karma-spec-reporter

npm install karma-spec-reporter --save-dev
複製程式碼

然後加入spec到karma.conf.js 的 reporters中:

reporters: ['spec']
複製程式碼

這樣就會生成這樣的形式報告:

image

其次我們需要新增istanbul進行程式碼覆蓋率測試。首先我們需要安裝一個babel外掛:

npm install babel-plugin-istanbul --save-dev 
複製程式碼

然後修改我們的.babelrc檔案:

{
  // ...
  "env": {
    "test": {
      "presets": ["env"],
      "plugins": ["istanbul"]
    }
  }
}
複製程式碼

通過使用.babelrc裡的env屬性,當我們通過設定BABEL_ENV=test的時候便可以去執行程式碼測試的相關外掛,這在複雜專案中的測試配置中很有用。 接下來,為了能生成覆蓋率測試報告,我們還需要安裝一個 karma Istanbul 外掛karma-coverage:

npm install karma karma-coverage --save-dev
複製程式碼

然後我們改一下package.json裡面的script指令碼:

"scripts": {
    "test": "cross-env BABEL_ENV=test karma start test/karma.conf.js"
}
複製程式碼

執行

npm run test
複製程式碼

開啟 coverage裡面的index.html檔案:

image

可以看到覆蓋率的一些測試結果。但是這樣有點不太方便,希望能可以直接在控制檯輸出就好了。所以我們需要改一下karma.conf.js檔案,加上這樣一個屬性:

coverageReporter: {
    dir: './coverage',
    reporters: [
        { type: 'lcov', subdir: '.' },
        { type: 'text-summary' } // 在控制檯輸出摘要
    ]
}
複製程式碼

這樣我們在控制檯便可以列印出基本的覆蓋率測試結果:

image

至此。已經完成了一個測試環境的搭建,可以滿足大部分工程的單測配置。

結語

可能平時由於大家更加關心業務程式碼,而會忽略一些前端的測試工作,網上的資料也是零零碎碎的,沒有一個系統的總結和介紹。這裡整理了一些測試工具的使用方法,希望可以對你有所幫助。然後本文所有程式碼和原始出處發表在個人部落格中。也歡迎閱讀之前的博文:

muwoo 的 blogs

單測demo

參考文章

測試框架 Mocha 例項教程

程式碼覆蓋率工具 Istanbul 入門教程

Node.js 單元測試:我要寫測試

關於前端開發談談單元測試

相關文章