測試庫:https://www.awesomes.cn/repos/Applications/Testingscss
/*************************************************************************************分割線************************************************************************************************************************************/html
步驟1、經過 vue init webpack-simple test-xxxxx安裝的簡單工程項目,在webpack-template項目中,須要手動安裝 "karma"、"karma-chrome-launcher"、"karma-coverage"、 "karma-mocha","karma-phantomjs-launcher"、"karma-phantomjs-shim","karma-sinon-chai"、"karma-sourcemap-loader"、"karma-spec-reporter"、"karma-webpack"、"mocha"、「chai」、"sinon"、"sinon-chai",可能須要安裝「cross-env」vue
cnpm i xxx --save-dev 便可node
步驟2、package.json中script腳本命令中添加"unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js",webpack
步驟3、由於咱們並非用腳手架集成引入測試框架(chai/Mocha等),因此項目目錄結構中不存在測試目錄,需咱們手動建立添加:git
1)與src、node_modules等文件夾同級結構中建立test目錄github
2)test目錄下建立unit文件夾web
3)unit目錄下存在3個文件(.eslintrc、index.js、karma.conf.js)&1個文件夾(specs)ajax
.eslintrc中內容:chrome
{
"env": {
"mocha": true
},
"globals": {
"expect": true,
"sinon": true
}
}
index.js中內容:
import Vue from 'vue'
Vue.config.productionTip = false
// require all test files (files that ends with .spec.js)
const testsContext = require.context('./specs', true, /\.spec$/)
testsContext.keys().forEach(testsContext)
// require all src files except main.js for coverage.
// you can also change this to match only the subset of files that
// you want coverage for.
const srcContext = require.context('../../src', true, /^\.\/(?!main(\.js)?$)/)
srcContext.keys().forEach(srcContext)
karma.conf.js中內容:
// This is a karma config file. For more details see
// http://karma-runner.github.io/0.13/config/configuration-file.html
// we are also using it with karma-webpack
// https://github.com/webpack/karma-webpack
var webpackConfig = require('../../webpack.test.conf')
module.exports = function (config) {
config.set({
// to run in additional browsers:
// 1. install corresponding karma launcher
// http://karma-runner.github.io/0.13/config/browsers.html
// 2. add it to the `browsers` array below.
//browsers: ['PhantomJS'],
browsers: ['Chrome'],
frameworks: ['mocha', 'sinon-chai', 'phantomjs-shim'],
reporters: ['spec', 'coverage'],
files: ['./index.js'],
preprocessors: {
'./index.js': ['webpack', 'sourcemap']
},
webpack: webpackConfig,
webpackMiddleware: {
noInfo: true
},
coverageReporter: {
dir: './coverage',
reporters: [
{ type: 'lcov', subdir: '.' },
{ type: 'text-summary' }
]
}
})
}
/***********end**************/
specs文件夾中存放測試腳本文件,
例如,在src中component中有個HelloWorld.vue/或者untils下HelloFarmFriend.js文件;注,此時在specs目錄下對應存在測試腳本文件,如HelloWorld.spec.js 或者HelloFarmFriend.spec.js
正以下文舉例所描述那樣。
步驟4、須要添加webpack.test.conf.js文件(與src同級),內容:
此時,還須要cnpm i --save-dev webpack-merge,理由是webpack配置須要的
最後,添加config目錄(與src同級)
1.dev.env.js中
2.prod.env.js
3.test.env.js
大功告成!試試
步驟5、完成上述步驟後,運行npm run unit命令便可。運行後會在test下unit目錄中生成一個coverage目錄,此文件爲代碼測試覆蓋率結果,找到並打開index.html便可查看測試報告。
src/components/HelloWorld.vue
<template>
<div class="hello">
<h1>{{ msg }}</h1>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data () {
return {
msg: 'dachengz Vue.js App'
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
此時測試腳本文件爲:HelloWorld.spec.js
import Vue from 'vue'
import HelloWorld from '../../../src/components/HelloWorld'
describe('Hello大橙子', () => {
it('should render correct contents', () => {
const Constructor = Vue.extend(HelloWorld)
const vm = new Constructor().$mount()
expect(vm.$el.querySelector('.hello h1').textContent)
.to.equal('dachengz Vue.js App')
})
})
上述本身建立對應文件,保存
/****佔位utils.js測試***/
1.建立一個utils目錄,添加一個add.js(demo)
function add(x,y){
return x + y
}
module.exports = add
2.一樣操做,在test/unit/specs目錄中添加add.spec.js文件
// add.test.js
import add from '../../../utils/add'
describe('測試加法',() => {
it('1加1 應該等於2', () => {
expect(add(1,1)).to.be.equal(2);
})
})
上述就是針對純待測js文件進行單元測試,保存代碼,運行npm run unit便可,結果以下圖:
/*********************************************************************************分割線*********************************************************************************************************************/
應用組件功能測試
組件測試vue-TDD的開發流程 (TDD什麼意思?下面有介紹)
{
1.編寫組件測試-在單元測試中將設計組件的名稱、屬性接口、事件接口、用斷言工具肯定衡量這個組件正確的標準
2.編寫組件代碼-以單元測試爲引導程序,編寫組件真實的實現代碼,讓測試經過
3.運行測試,並看到測試經過
4.重構
}
測試工具:
測試管理工具(測試加載器) Karma
{
Karma-自動化測試程序的入口,執行如下任務
1.爲測試程序注入指定依賴包
2.可同時在一個或多個瀏覽器宿主中執行測試,知足兼容性測試需求
3.執行代碼覆蓋性測試
4.輸出測試報告
5.執行自動化測試
}
測試框架 Mocha (測試Node和瀏覽器js代碼)
vue官網單元測試框架 Vue Test Utils
斷言庫 chai / sinon
{
expect和should是BDD風格,使用相同鏈式語言組織斷言,不一樣點在於初始化斷言方式:
1.expect使用構造函數來建立斷言對象實例
2.should經過爲Object.prototype新增方法來實現斷言(should不支持IE); expect直接指向chai.expect
而should則是chai.should()
注:在構建vue-cli工程時,添加了單元測試是不須要手動配置chai;
chai和sinon 被Karma經過Karma-sinon-chai插件直接嵌入到單元測試的上下文中,不用import就可使用
}
那問題來了?sinon是什麼?輔助庫 sinon-chai
{
sinon: 負責仿真
三個方法: 調用偵測Spy / 接口仿真 Stub / 對象仿真 Mock
}
測試瀏覽器 chrome (PhantomJs 無界面瀏覽器,速度比chrome快)
測試覆蓋率統計工具 Karma-coverage
test文件夾下unit,單元測試相關文件
specs-中存放測試腳本(全部的測試文件, 都將放specs這個目錄下, 並以測試腳本名.spec.js結尾命名)
coverage文件裏存放測試報告,html打開的是代碼覆蓋率
Karma.config.js配置文件
npm run unit 輸出
demo:
1.被測試的組件HelloWorld.vue
<template>
<div class="hello">
<h1>Welcome to Your Vue.js App</h1>
</div>
</template>
2.測試腳本HelloWorld.spec.js
import HelloWorld from '@/components/HelloWorld';
import { mount, createLocalVue, shallowMount } from '@vue/test-utils'
describe('HelloWorld.vue', () => {
it('should render correct contents', () => {
const wrapper = shallowMount(HelloWorld);
let content = wrapper.vm.$el.querySelector('.hello h1').textContent;
expect(content).to.equal('Welcome to Your Vue.js App');
});
});
describe是"測試套件",表示一組相關的測試。它是一個函數,
第一個參數是測試套件的名稱("加法函數的測試"),
第二個參數是一個實際執行的函數。
it是"測試用例"(test case),表示一個單獨的測試,是測試的最小單位。
它也是一個函數,第一個參數是測試用例的名稱,第二個參數是一個實際執行的函數。
全部的測試用例(it塊)都應該含有一句或多句的斷言
斷言(判斷源碼的實際執行結果與預期結果是否一致),若是不一致, 就會拋出錯誤
expect(content).to.equal('Welcome to Your Vue.js App')實際爲變量content應等於'Welcome to Your Vue.js App'
Vue的腳手架提供的斷言庫是sino-chai, 是一個基於Chai的斷言庫
chai官方文檔:https://www.chaijs.com/
chai官方文檔翻譯:https://www.jianshu.com/p/f200a75a15d2
注意:Mocha測試鉤子
在describe塊中提供了四個鉤子: before(), after(), beforeEach(), afterEach(). 它們會在如下時間執行:
describe('鉤子說明', function() { // 第一個參數提示要測試什麼;第二個匿名函數
before(function() {
// 在全部場景執行以前執行(執行一次)
});
after(function() {
// 在全部場景執行以後執行(執行一次)
});
beforeEach(function() {
// 在每一個場景測試執行以前執行
});
afterEach(function() {
// 在每一個場景執行完成以後執行
});
});
舉個例子:
describe('Array', () => {
let expectTarget = []
beforeEach(() => {
// 在每一個場景測試執行以前執行
expectTarget.push(1)
});
afterEach(() => {
// 在每一個場景執行完成以後執行
expectTarget = [] // 清空
});
it('應該存在一個爲1的整數', () => {
expect(expectTarget[0]).to.eqls(1)
});
it('應該存在多個的指望值檢測', () => {
expect(expectTarget[0]).to.eqls(1)
expect(true).to.eqls(true)
});
});
Mocha 官方文檔:https://mochajs.org/
Mocha 官方文檔翻譯:https://www.jianshu.com/p/9c78548caffa
阮一峯 - 測試框架 Mocha 實例教程:http://www.ruanyifeng.com/blog/2015/12/a-mocha-tutorial-of-examples.html
vue-test-utils官方文檔:https://vue-test-utils.vuejs.org/zh/api/#render
vue-test-utils經常使用api
find(): 返回匹配選擇器的第一個DOM節點或Vue組件的wrapper, 可使用任何有效的選擇器
text(): 返回wrapper的文本內容
html(): 返回wrapper DOM的HTML字符串
trigger(): 在該 wrapper DOM 節點上觸發一個事件
setData(): 設置data的屬性並強制更新
it('find()/text()/html()方法', () => {
const wrapper = mount(Counter);
const h3 = wrapper.find('h3');
expect(h3.text()).to.equal('Counter.vue');
expect(h3.html()).to.equal('<h3>Counter.vue</h3>');
})
it('trigger()方法', () => {
const wrapper = mount(Counter);
const buttonOfSync = wrapper.find('.sync-button');
buttonOfSync.trigger('click');
buttonOfSync.trigger('click');
const count = Number(wrapper.find('.num').text());
expect(count).to.equal(2);
})
it('setData()方法',() => {
const wrapper = mount(Counter);
wrapper.setData({foo: 'bar'});
expect(wrapper.vm.foo).to.equal('bar');
})
遇到vue-異步測試狀況
Mocha異步測試在it內加一個done函數,在全部的斷言執行完成後調用done()就能夠釋放測試用例並告知Mocha測試的執行結果
例子:一個User對象,具備一個save方法,這個方法經過ajax將數據保存到服務器端,若服務端沒有返回錯誤,即認爲這個save方法是成功的
describe('User', () => {
describe('#save()方法', () => {
it('應該成功保存到服務器端且不會返回任何錯誤信息', done => {
const user = new User('納尼')
user.save( err => {
if (err) done(err) // 若是返回錯誤碼直接將錯誤碼輸出到控制檯
else done()
})
})
})
})
優化,將done做爲回調參數使用
describe('User', () => {
describe('#save()方法', () => {
it('應該成功保存到服務器端且不會返回任何錯誤信息', done => {
const user = new User('納尼')
user.save(done)
})
})
})
使用promise的另外一種替代方案就是將chai斷言做爲it的返回值,將chai斷言做爲一個promise對象返回
讓Mocha進行鏈式處理,須要藉助chai-as-promised庫支持(https:www.npmjs.com/package/chai-as-promised)
在Mocha v3.0.0以後的版本中,若是直接構造ES6上的Promise對象則會被Mocha認爲是非法的
it('應該完成此測試', done => {
return new Promise( resolve => {
assert.ok(true)
resolve()
}).then(done)
})
會提示異常信息
擴展:基於Nightwatch的端到端測試環境
端到端測試,簡稱e2e (End to End test):
***側重於檢測界面的交互效果與操做邏輯是否正確
{
1.單元測試側重:檢驗函數的輸出結果
2.e2e側重:從用戶視角,對真實系統的訪問行爲進行仿真
}
簡單理解:
1.單元測試的功能只能確保單個組件的質量,沒法測試具體的業務流程是否運做正常
2.e2e是面對 組件與組件之間、用戶與真實環境之間的一種集成性測試
demo: https://github.com/Lee-Tanghui/Vue-Testing-Demo/issues
組件單元測試:https://blog.csdn.net/hsany330/article/details/73650020
***更多請參考官網例子:https://cn.vuejs.org/v2/guide/unit-testing.html補充下:具體去google一下Test-Driven Development(TDD)即測試驅動開發,一種測試先於編寫代碼的思想用於指導軟件開發Behavior Driven Development(BDD)行爲驅動開發是一種敏捷軟件開發的技術單元測試(白盒測試),主要用於測試開發人員編寫的代碼是否正確,這部分工做都是開發人員本身來作的;一般而言,一個單元測試是用於判斷某個特定條件(或者場景)下某個特定函數的行爲BDD(灰盒測試、黑盒測試)