一. 前端自動化測試
大多數前端開發者對測試相關的知識是比較缺乏的,一來是開發節奏很快,來不及寫,另一方面團隊裡也配備了“人肉測試機”,完全沒必要自己來。但随着項目體量的增大,許多人維護同一份代碼,經常會出現有些函數莫名其妙地結果不對了,或者某個接口的入參變了,又或者哪位大哥把後端傳回的資料結構給改了。每天工作的時間裡被拉來拉去幫人定位問題,結果花了很多時間卻發現大部分都是别人的鍋。每當遇到項目上線,那就更熱鬧了,跟着其他“人肉測試機”大家一起點點點......
很多團隊都有個通病,凡是出了問題,先往前端身上推,然後前端各種檢測排查,到最後是誰的鍋,誰呵呵一笑然後領走就完了,如果你也曾是以心裡印下了不計其數的草泥馬的馬蹄印,那我強烈建議你學習【前端自動化測試】相關的知識,或許你不會經常用到它,但在組内互怼和甩鍋的環節絕對能助你一臂之力。當然總有一天,你會發現測試的價值遠不止這樣。它對大型項目提供的可靠性保障是人力無法比拟的。
前端很流行這樣一句話:如果你覺得單元測試沒什麼用,隻能說明你做的項目不夠大。
二. 工具簡介
2.1 Karma
官方網址:https://karma-runner.github.io/2.0/index.html
Karma
為前端自動化測試提供了跨浏覽器測試的能力,可以自動在
Chrome
,
Firefox
IE
等主流浏覽器依次跑完測試用例,同時也支援headless浏覽器(入
phantomJs
)中運作測試用例。
webpack
+
babel
可以主動為想要适配的浏覽器提供轉碼和墊片更新檔引入能力,而
Karma
可以為最終的結果提供驗證能力。
Karma
的配置方式可以閱讀《webpack4.0各個擊破(9)——Karma篇》進行了解。
2.2 Mocha
Mocha
是前端自動化測試架構,測試架構需要解決相容不同風格斷言庫,測試用例分組,同步異步測試架構,生命周期鈎子等架構級的能力。
-
的基本文法Mocha
describe('我現在要測某一個頁面的幾個功能',function(){
describe('現在要測XX功能',function(){
it('某個變量的值應該是數字',function(){
//寫斷言
})
});
describe('現在要測YY功能',function(){
it('某個數組長度應該不小于10',function(){
//寫斷言
})
});
})
- 異步測試文法
describe('現在要測XX功能',function(){
it('某個變量的值應該是數字',function(done){
//寫斷言
//手動調用done()表示異步結束,類似于Promise中的resolve
})
});
-
不同風格的斷言庫
支援
should.js
及node核心斷言子產品expect.js
等。assert
-
生命周期鈎子
生命周期鈎子一般用來建立和清理環境或全局變量。
describe('hooks', function() {
before(function() {
// runs before all tests in this block
});
after(function() {
// runs after all tests in this block
});
beforeEach(function() {
// runs before each test in this block
});
afterEach(function() {
// runs after each test in this block
});
// test cases
});
2.3 Chai
Chai
是一個斷言庫合集,支援
expect
assert
should
斷言文法,非專業測試崗位其實沒必要深究,了解使用方法就可以了。使用示例:
expect(bar).to.not.exist;//斷言變量bar不存在
expect(data).to.have.ownProperty('length');//斷言data有length屬性
expect(name).to.be.a('string');//斷言name是一個字元串
assert.equal(value1,value2);//斷言value1和value2相等
Tim.should.be.an.instanceof(Person);//斷言Tim是Person類的執行個體
上面的文法在引入了
Chai
後都是支援的,當斷言不成立時,結果報告中會給出明确标記。
三. 基于Chai的自動化單元測試
單元測試的原理并不算複雜,相當于另外編寫了一套程式,把業務邏輯中的腳本檔案當做子產品引入,模拟其運作環境(例如需要的浏覽器類型,全局變量等),然後使用一組或若幹組覆寫不同使用場景的參數來調用想要測試的函數單元,并判斷函數傳回的結果是否和預期的相同。
簡單地說,自動化測試工具隻是取代了一個照着Excel表格測試并記錄結果的人力資源。
測試用例檔案的基本寫法:
var chai = require('chai');//引入斷言庫
var expect = chai.expect;//使用expect文法
//引用源代碼中的業務邏輯子產品;
var ColorFac = require('../../../../src/components/Example/colorFac');
describe("ColorFac Module Test", function () {
it("should return a luminanced color", function () {
//調用源代碼中業務邏輯子產品中的方法;
var color = ColorFac.luminate("#fff", "-0.5");
//編寫測試斷言
expect(color).is.not.empty;
});
});
單元測試報告:
使用Webpack + Karma + Mocha + Chai進行自動化測試(單元測試+代碼覆寫率)的方法可以檢視《webpack4.0各個擊破(9)——Karma篇》。
四. 基于Chai-http的自動化接口測試
Chai-Http
是基于
Chai
擴充的插件,可用于測試與
http
請求相關的邏輯代碼。開發中也可以利用
PostMan
或是
DocLever
來管理接口并進行接口測試。接口測試的運作方式和單元測試很類似,差別在于測試用例的寫法。假設接口測試的用例都寫在/test/apis/apis.js中,配置方式如下:
Karma.api.conf.js
:
var path = require('path');
module.exports = function(config) {
config.set({
files: [
'test/apis/apis.js'
],
// frameworks to use
frameworks: ['mocha'],
preprocessors: {
// only specify one entry point
// and require all tests in there
'test/apis/apis.js': ['webpack']
},
reporters: ['mocha'],
webpack: {
mode: 'none',
},
webpackMiddleware: {
noInfo: true
},
plugins: [
require("karma-webpack"),
require("karma-mocha"),
require("karma-chai"),
require("karma-chrome-launcher"),
require("karma-mocha-reporter"),
],
browsers: ['Chrome']
});
};
/test/apis/apis.js
:(測試用例的語義化非常明顯,代碼基本不需要解釋)。
var chai = require('chai');
var chaiHttp = require('chai-http');
var expect = chai.expect;
chai.use(chaiHttp);
//define address
const ADDRESS = "http://localhost:3001";
//open a http connection
var requester = chai.request(ADDRESS);
describe('清單服務APIS測試',function () {
it('GET /healthmap/chart1 應該傳回包含legendData字段的資料',function (done) {
chai.request(ADDRESS).get('/healthmap/chart1')
.end((err,res)=>{
var data = JSON.parse(res.text);
expect(res).to.have.status(200);
expect(data.data).to.have.ownProperty('legendData');
done();
})
});
it('GET /operationdashboard/systemwarn 應該傳回字元串',function (done) {
chai.request(ADDRESS).get('/operationboard/systemwarn')
.end((err,res)=>{
var data = JSON.parse(res.text);
expect(res).to.have.status(200);
expect(data.data).to.be.a('String');
done();
})
});
})