首先我們來看下什么是BDD双藕,什么是TDD
測試風(fēng)格:測試驅(qū)動開發(fā)(Test-Driven Development,TDD)、(Behavior Driven Development,BDD)行為驅(qū)動開發(fā)均是敏捷開發(fā)方法論浆劲。 TDD關(guān)注所有的功能是否被實(shí)現(xiàn)(每一個功能都必須有對應(yīng)的測試用 例),suite配合test利用assert('tobi' == user.name); BDD關(guān)注整體行為是否符合整體預(yù)期,編寫的每一行代碼都有目的提 供一個全面的測試用例集。expect/should,describe配合it利用自然語 言expect(1).toEqual(fn())執(zhí)行結(jié)果丢烘。-
接下來我們看下單元測試的運(yùn)行流程
image.png
來開始一個單元測試
unit測試是把代碼看成是一個個的組件俱箱。從而實(shí)現(xiàn)每一個組件的單獨(dú)測試国瓮,測試內(nèi)容主要是組件內(nèi)每一個函數(shù)的返回結(jié)果是不是和期望值一樣。
這里我們使用karma,官網(wǎng)地址為: karma: https://karma-runner.github.io/latest/index.html
安裝karma:
karma-jasmine jasmine-core是斷言庫
npm install karma --save-dev
npm install karma-jasmine karma-chrome-launcher jasmine-core --save-dev
然后執(zhí)行karma init
這里會有很多步選擇狞谱,我們都默認(rèn)就好了乃摹,但是選擇瀏覽器時我們選擇phantomjs
做完這些操作后,會生成一個karma.conf.js
文件如下:
module.exports = function(config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
// 解析所有模式的基本路徑
basePath: '',
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
// 框架的使用
frameworks: ['jasmine'],
files: [
"unit/**/*.js", // 需要被測試的文件
"unit/**/*.spec.js" // 測試文件
],
// list of files / patterns to exclude
// 要排除的文件/模式列表
exclude: [
],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
// 在將匹配的文件提供給瀏覽器之前跟衅,對它們進(jìn)行預(yù)處理
preprocessors: { // 哪些文件需要通過覆蓋率檢查
'unit/**/*.js': ['coverage']
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
// 使用測試結(jié)果報告程序
reporters: ['progress','coverage'], // 進(jìn)度孵睬,覆蓋率(需要裝karma-coverage)
coverageReporter: { // 生成的測試報表
type : 'html',
dir : 'docs/coverage/'
},
// web server port
// 測試端口號
port: 9876,
// enable / disable colors in the output (reporters and logs)
// 在輸出中啟用/禁用顏色(記者和日志)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
// 日志相關(guān)配置
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
// 啟用/禁用監(jiān)視文件并在任何文件更改時執(zhí)行測試(熱更新)
autoWatch: true,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['PhantomJS'], // 選擇的瀏覽器
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
// 持續(xù)集成模式
singleRun: true, // 使用無頭瀏覽器這里必須設(shè)置為true
// Concurrency level
// how many browser should be started simultaneous
// 并發(fā)級別
concurrency: Infinity
})
}
我們在util文件夾中新建一個文件index.js
對其進(jìn)行測試,內(nèi)容如下
window.add = function (a) {
return a + 1;
}
再新建一個測試文件index.spec.js
(或indexSpec.js)
escribe("測試基本函數(shù)的api",function () {
it("+1函數(shù)的應(yīng)用",function(){
expect(window.add(1)).toBe(2)
})
})
這時就可以跑執(zhí)行karma start
開始測試了
到這里有一個問題,如果代碼中有if,else怎么辦伶跷。這時我們就需要代碼覆蓋率檢查了掰读。下載 karma-coverage,如果報錯可能是缺了karma-phantomjs-launcher和phantomjs這兩個包秘狞。在上面的配置文件中我已經(jīng)進(jìn)行了配置,現(xiàn)在直接跑就ok了蹈集。
頁面測試: e2e
e2e測試是把我們的程序堪稱是一個黑盒子烁试,我不懂你內(nèi)部是怎么實(shí)現(xiàn)的,我只負(fù)責(zé)打開瀏覽器拢肆,把測試內(nèi)容在頁面上輸入一遍减响,看是不是我想要得到的結(jié)果。
這里我們使用selenium-webdriver
來進(jìn)行e2e測試郭怪,e2e測試需要一個瀏覽器的執(zhí)行環(huán)境辩蛋,所以首先在selenium-webdriver官網(wǎng)上下載瀏覽器驅(qū)動,然后安裝selenium-webdriver
在e2e文件夾中新建一個baidu.spec.js
文件移盆,內(nèi)容如下
const {Builder, By, Key, until} = require('selenium-webdriver');
(async function example() {
let driver = await new Builder().forBrowser('firefox').build();
try {
await driver.get('http://www.baidu.com/'); // 要測試網(wǎng)站的網(wǎng)址
await driver.findElement(By.name('wd')).sendKeys('webdriver', Key.RETURN); // 通過名class名為wd的input框搜索webdriver
await driver.wait(until.titleIs('webdriver_百度搜索'), 1000); // 等待1s看title是否等于webdriver_百度搜索
} finally {
await driver.quit(); // 無論是否成功悼院,退出driver
}
})();
然后我們運(yùn)行: node ./e2e/baidu.spec.js
UI測試
PhantomCSS
是一個很好的UI測試插件,但是我們這里使用backstopjs
來做演示
安裝backstopjs后咒循,我們backstop init
這時會生成一個backstop_data
文件夾,和backstop.json
文件
我們首先來看下backstop_data
文件夾結(jié)構(gòu)
+-- backstop_data
+-- bitmaps_reference -- 放置ui切圖的文件夾
+-- bitmaps_test -- 生成的測試圖例地址
+-- engine_scripts
+-- casper -- 在無頭瀏覽器中操作鼠標(biāo)
+-- chromy
+-- puppet
+-- cookies.json -- 如果網(wǎng)頁需要配置cookie在這里配置
再來看下backstop.json
文件配置
{
"id": "yd", // 項(xiàng)目名稱
"viewports": [ // 設(shè)備配置項(xiàng)
{
"label": "phone",
"width": 375,
"height": 667
},
{
"label": "tablet",
"width": 1024,
"height": 768
}
],
"onBeforeScript": "puppet/onBefore.js", // 引擎据途, 在engine_scripts文件夾中
"onReadyScript": "puppet/onReady.js", // 同上
"scenarios": [ // 配置截圖
{
"label": "qqmap",
"cookiePath": "backstop_data/engine_scripts/cookies.json", // cookie地址
"url": "https://map.qq.com/m/", // 要被測試頁面地址
"referenceUrl": "",
"readyEvent": "",
"readySelector": "",
"delay": 0,
"hideSelectors": [],
"removeSelectors": [],
"hoverSelector": "",
"clickSelector": "",
"postInteractionWait": 0,
"selectors": [],
"selectorExpansion": true,
"expect": 0,
"misMatchThreshold" : 0.1,
"requireSameDimensions": true
}
],
"paths": {
"bitmaps_reference": "backstop_data/bitmaps_reference", // 美工圖放的地址
"bitmaps_test": "backstop_data/bitmaps_test", // 生成測試圖的地址
"engine_scripts": "backstop_data/engine_scripts", // 默認(rèn)js引擎
"html_report": "docs/backstop_data/html_report", // 生成報表地址
"ci_report": "docs/backstop_data/ci_report" // ci報表
},
"report": ["browser"],
"engine": "puppeteer",
"engineOptions": {
"args": ["--no-sandbox"]
},
"asyncCaptureLimit": 5,
"asyncCompareLimit": 50,
"debug": false,
"debugWindow": false
}
最后執(zhí)行backstop test進(jìn)行測試
接口測試
接口測試我們這里使用mocha
首先安裝mocha
我們新建一個app.js
文件,對app.js文件的內(nèi)容進(jìn)行測試
var express = require("express");
var app = express();
app.get("/test",function(req,res){
res.send({
result:"Hello World"
})
});
var server = app.listen(3000,function(){
console.log("demo app 啟動");
});
module.exports = app;
我們再創(chuàng)建測試文件router.spec.js
const axios = require("axios");
describe("node接口", function () {
it("test接口測試", function (done) {
axios.get('http://localhost:3000/test')
.then(function (response) {
if (response.data.result == "Hello World") {
done();
} else {
done(new Error("數(shù)據(jù)請求格式錯誤"));
}
})
.catch(function (error) {
done(error);
});
});
});
// 高級寫法
const superagent = require("superagent");
const app = require("./app");
function request(){
return superagent(app.listen())
}
request()
.get("/test")
.expect("Content-Type",/json/)
.expect(200)
.end(function(err,res){
if(res.data == "Hello World"){
done();
}else{
done(err);
}
})
最后新建mochaRunner.js
文件如下叙甸,這里我們需要下載mochawesome
來生成報表
const Mocha = require("mocha");
const mocha = new Mocha({
reporter: 'mochawesome', // 安裝mochawesome(生成報表的包)
reporterOptions:{
reportDir:"./docs/mochawesome-reporter" // 生成報表位置
}
});
mocha.addFile("./service/router.spec.js");
mocha.run(function(){
console.log("done");
process.exit(); // 退出進(jìn)程颖医,不退出會卡死
});
這時我們運(yùn)行node ./mochaRunner.js
如果我們需要將上面的的一起測試,只需要: npm run unit && npm run e2e && npm run ui
就OK了
JavaScript Lint&Hint
目的:檢測JavaScript代碼標(biāo)準(zhǔn) 原因:JavaScript代碼詭異裆蒸,保證團(tuán)隊(duì)代碼規(guī)范 lint:http://www.jslint.com/
hint: http://www.jshint.com/
搭配自動化任務(wù)管理工具完善自動化測試grunt- jshint熔萧、grunt-jslint
下面來介紹一些工程用的測試包
- nightwatchjs(vue專用)
- rize+puppeteer+rize
- 阿里的f2etest(自動化錄制)
這里我們需要注意下,在f2etest中有UI recorder僚祷,這個東西會打開兩個瀏覽器佛致,因?yàn)橛行┚W(wǎng)站像驗(yàn)證碼每次都不一樣,第二臺瀏覽器會與第一個比較辙谜,結(jié)果相同就算成功