淺析前端vue項目使用單元測試框架一

 

測試庫: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同級),內容:

'use strict'
// This is the webpack config used for unit tests.

// const utils = require('./utils')
const webpack = require('webpack')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.config')

const webpackConfig = merge(baseWebpackConfig, {
// use inline sourcemap for karma-sourcemap-loader
module: {
// rules: utils.styleLoaders()
},
devtool: '#inline-source-map',
resolveLoader: {
alias: {
// necessary to to make lang="scss" work in test when using vue-loader's ?inject option
// see discussion at https://github.com/vuejs/vue-loader/issues/724
'scss-loader': 'sass-loader'
}
},
plugins: [
new webpack.DefinePlugin({
'process.env': require('./config/test.env')
})
]
})

// no need for app entry during tests
delete webpackConfig.entry

module.exports = webpackConfig

 

此時,還須要cnpm i --save-dev webpack-merge,理由是webpack配置須要的

 

最後,添加config目錄(與src同級)

1.dev.env.js中

'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')

module.exports = merge(prodEnv, {
NODE_ENV: '"development"'
})

 

2.prod.env.js

'use strict'
module.exports = {
NODE_ENV: '"production"'
}

 

3.test.env.js

'use strict'
const merge = require('webpack-merge')
const devEnv = require('./dev.env')

module.exports = merge(devEnv, {
NODE_ENV: '"testing"'
})

大功告成!試試

 

步驟5、完成上述步驟後,運行npm run unit命令便可。運行後會在test下unit目錄中生成一個coverage目錄,此文件爲代碼測試覆蓋率結果,找到並打開index.html便可查看測試報告。

 

這裏先以vue單文件組件爲例子

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測試***/

這裏先以須要測試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(灰盒測試、黑盒測試)

相關文章
相關標籤/搜索