從零開始作Vue前端架構(6)單元測試 & 代碼覆蓋率

新的一年

以前由於上家公司的經營出了問題,年前的大裁人,過了一個漫長的春節。
以後加入了新公司,而後正好遇上一個很緊急的項目,忙成狗,所以很久沒更新文章了。
不過,我又回來啦!javascript

前言

自動化測試,咱們將使用karma和nightmare,內容會包括:html

  1. 單元測試
  2. e2e測試(放下一篇文章)

其實,單元測試通常用在寫公共包的時候,好比通用的js函數庫,通用的UI組件庫。基本不太會在作業務項目的時候還使用單元測試。
而後,e2e測試的話,那其實每每是測試工程師須要作的,每每使用selenium。
那難道前端就不須要學測試了嗎?
答案確定是否認的,否則我寫個毛......
vue的項目就用了單元測試和e2e。
基於vue的UI組件庫,好比:餓了麼的element、滴滴的cube-ui、有讚的vant等都有單元測試(咩有e2e,由於不必)。
滴滴的話,我問了下黃軼大佬,他們項目前端自動化測試是用了單元測試和e2e的。
總之,兩種都是由應用場景的,e2e雖然用的很少,或者說有時候不是前端自動化的範疇,可是其實作起來很簡單,學會準沒錯!前端

1、單元測試

安裝karma

npm install karma --save-dev
npm install karma-jasmine karma-chrome-launcher jasmine-core --save-dev

package.jsonscripts配置
"test": "karma start"vue

初始化karma

$ karma init my.conf.js

Which testing framework do you want to use?
Press tab to list possible options. Enter to move to the next question.
> mocha

Do you want to use Require.js?
This will add Require.js plugin.
Press tab to list possible options. Enter to move to the next question.
> no

Do you want to capture a browser automatically?
Press tab to list possible options. Enter empty string to move to the next question.
> Chrome
>

What is the location of your source and test files?
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".
Press Enter to move to the next question.
> test/**/*.js
>

Should any of the files included by the previous patterns be excluded?
You can use glob patterns, eg. "**/*.swp".
Press Enter to move to the next question.
>

Do you want Karma to watch all the files and run the tests on change?
Press tab to list possible options.
> yes

初始成功之後,會生成一份karma.conf.js配置文件。java

順便,在根目錄建立一個test的文件夾,在這文件夾中建立一份index.js,內容爲:node

describe('A spec suite', function() {
  it('contains a passing spec', function() {
    console.log('Hello Karma')
  })
})

運行一下:
npm run test
咱們會看到程序會自動打開chrome瀏覽器,而後顯示測試結果。webpack

正式的寫個單元測試

從新組織test文件夾

.
└── unit
    ├── index.js
    ├── karma.conf.js
    └── specs
        └── dom.spec.js

由於咱們還要作e2e測試,因此,在test下,用各個文件夾區分,unit下就是單元測試的內容了。git

安裝一系列包

karma-webpack
karma-sourcemap-loader
karma-coverage
chai
sinon
sinon-chai
karma-sinon-chai
karma-mocha-reporter

karma-webpack:由於咱們的項目代碼是用es6或者es7寫的,因此webpack編譯是必須的
karma-sourcemap-loader:sourcemap明顯是必要的
karma-coverage:作代碼覆蓋率的時候須要用
chai:搭配mocha斷言
sinon:搭配mocha作spy、stub、mock
sinon-chai:用chai來作sinon的斷言,能夠說是擴展
karma-sinon-chai:方便在測試代碼中的調用,直接就能用expect、sinon.spy等,不須要每一個文件都import
karma-mocha-reporter:mocha測試完的報告es6

像karma、mocha、chai、sinon這種測試工具,我不會很詳細的介紹,所有都介紹的話內容實在有點多,並且也比較枯燥。想要學習能夠看個人參考資料,是我花了大把時間篩選整理出來的。github

修改karma.conf.js

const webpackConfig = require('../../webpack.config.test.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: ['mocha', 'sinon-chai'],

    // list of files / patterns to load in the browser
    files: [
      './index.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
    preprocessors: {
      './index.js': ['webpack', 'sourcemap', 'coverage']
    },

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['mocha', 'coverage'],

    coverageReporter: {
      dir: './coverage',
      reporters: [
        { type: 'lcov', subdir: '.' },
        { type: 'text-summary' }
      ]
    },
    .
    .
    .
    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: true,

    // Concurrency level
    // how many browser should be started simultaneous
    concurrency: Infinity,

    webpack: webpackConfig,
    webpackMiddleware: {
      stats: 'errors-only'
    }
  })
}

karma本來在根目錄,咱們直接移過來就行了。而後修改的很少,我稍微解釋一下:

  1. files:將要被測試的文件
  2. preprocessors:在引入文件前,須要用什麼方式處理,咱們看到了,包括webpack、sourcemap、coverage
  3. reporters:測試完成後的報告,咱們須要mocha的報告和coverage的報告
  4. coverageReporter:代碼覆蓋率生成的報告文件地址和存在形式設置
  5. webpack:在這須要引入webpack的配置,咱們見到頂部,引入了webpack.test.config.js文件,咱們待會兒會介紹裏面的配置
  6. webpackMiddleware:stats: 'errors-only'咱們讓webpack的編譯過程不顯示出來,除非編譯報錯

配置webpack.test.config.js

const webpackConfigBase = require('./webpack.config.base.js')

const config = Object.assign(webpackConfigBase.config, {
  // sourcemap 模式
  devtool: '#inline-source-map',
  module: {
    rules: [
      {
        test: /\.js$/,
        loader: 'babel-loader',
        exclude: /node_modules/
      }
    ]
  },
})

module.exports = config
``

#### 編輯index.js入口文件
這個文件是爲了配合`karma-webpack`的,詳情見[Alternative Usage](https://github.com/webpack-contrib/karma-webpack#alternative-usage)

// require all test files (files that ends with .spec.js)
// 語法說明:https://doc.webpack-china.org...
const testsContext = require.context('./specs', true, /.spec$/)
testsContext.keys().forEach(testsContext)

// require all src files which in the app/common/js for coverage.
// you can also change this to match only the subset of files that
// you want coverage for.
const srcContext = require.context('../../app/common/js/', true, /.js$/)
srcContext.keys().forEach(srcContext)

測試代碼放在`./specs`文件夾下,被測試原文件在`../../app/common/js/`下。這裏我只測試一些公共的js文件,若是你須要測試其它,可自行修改。好比一些基於vue的UI組件庫,你想要測試全部組件代碼,還須要作些配置上的修改,這方面能夠參考滴滴的cube-ui項目,挺完整的,覆蓋率也很高。

#### 正式寫測試代碼
編輯`dom.spec.js`文件:

/**

  • 測試common/utils/dom.js

*/
import * as dom from 'common/js/utils/dom.js'

// const expect = require('chai').expect 裝過sinon-chai就不須要這句了;sinon同理

describe('utils/dom', () => {
// 測試hasClass
it('hasClass', () => {

const ele = document.createElement('div')
ele.className = 'base kitty'
// 是否含有base
expect(dom.hasClass(ele, 'base')).to.be.equal(true)
// 是否含有kitty
expect(dom.hasClass(ele, 'kitty')).to.be.equal(true)
// 是否含有tom
expect(dom.hasClass(ele, 'tom')).to.be.equal(false)
// 無參數
expect(dom.hasClass()).to.be.equal(false)

})
// 測試addClass
it('addClass', () => {

const ele = document.createElement('div')
ele.className = 'base'
// 增長類名kitty
dom.addClass(ele, 'kitty')
expect(ele.className).to.be.equal('base kitty')
// 再增長類名kitty,但願並不會有重複類名
dom.addClass(ele, 'kitty')
expect(ele.className).to.be.equal('base kitty')

})
// 測試removeClass
it('removeClass', () => {

const ele = document.createElement('div')
ele.className = 'base kitty'
// 刪除類名kitty
dom.removeClass(ele, 'kitty')
expect(ele.className).to.be.equal('base')
// 刪除不存在的類名
dom.removeClass(ele, 'tom')
expect(ele.className).to.be.equal('base')

})
// 測試noce
it('once', () => {

const ele = document.createElement('div')
const callback = sinon.spy()
dom.once(ele, 'click', callback)
// 點擊一次
ele.click()
expect(callback).to.have.been.calledOnce
// 再點擊一次,預期應該是不調用callback的,因此依然爲calledOnce
ele.click()
expect(callback).to.have.been.calledOnce

})
})

代碼註釋已經很清楚啦~

#### 運行測試
先修改下`package.json`配置:`"test:unit": "karma start test/unit/karma.conf.js"`
運行:

➜ construct git:(master) npm run test:unit

> vue-construct@1.0.0 test:unit /Users/Terry/WFE/vue-study/construct
> karma start test/unit/karma.conf.js

START:
ℹ 「wdm」:
ℹ 「wdm」: Compiled successfully.
ℹ 「wdm」: Compiling...
ℹ 「wdm」:
ℹ 「wdm」: Compiled successfully.
23 04 2018 01:25:39.438:INFO [karma]: Karma v2.0.0 server started at http://0.0.0.0:9876/
23 04 2018 01:25:39.440:INFO [launcher]: Launching browser Chrome with unlimited concurrency
23 04 2018 01:25:39.448:INFO [launcher]: Starting browser Chrome
23 04 2018 01:25:41.778:INFO [Chrome 66.0.3359 (Mac OS X 10.13.2)]: Connected on socket A9ZeKTNtnUU9MAceAAAA with id 51610088
  utils/dom
    ✔ hasClass
    ✔ addClass
    ✔ removeClass
    ✔ once

Finished in 0.008 secs / 0.004 secs @ 01:25:41 GMT+0800 (CST)

SUMMARY:
✔ 4 tests completed
=============================== Coverage summary ===============================
Statements   : 87.12% ( 142/163 ), 14 ignored
Branches     : 61.25% ( 49/80 ), 22 ignored
Functions    : 86.11% ( 31/36 ), 5 ignored
Lines        : 90.79% ( 138/152 )

===============================

參考資料

karma 測試框架的前世此生
karma thesis
karma 官網
前端自動化測試工具overview
前端自動化測試解決方案探析
mocha官網
代碼測試覆蓋率分析
聊一聊前端自動化測試
Sinon指南: 使用Mocks, Spies 和 Stubs編寫JavaScript測試
sinon-chai github

論文是個頗有意思的東西,看多了你會驚人地發現,不少大廠有深度的文章其實都是對論文的純翻譯~
另外還參考了vue和滴滴的cube-ui的項目測試部分。

項目完整代碼

Vue前端架構-by 子咻

相關文章
相關標籤/搜索