前端單元測試

爲保證代碼的質量,單元測試必不可少。本文記錄本身在學習單元測試過程當中的一些總結。css

TDD與BDD的區別

TDD屬於測試驅動開發,BDD屬於行爲驅動開發。我的理解其實就是TDD先寫測試模塊,再寫主功能代碼,而後能讓測試模塊經過測試,而BDD是先寫主功能模塊,z再寫測試模塊。詳見示例html

服務端代碼測試

所謂服務端代碼,指的就是一個node的模塊,能在node的環境中運行。以一個項目爲例,代碼結構以下:前端

.
├── index.js
├── node_modules
├── package.json
└── test
    └── test.js

前端測試框架主要是MochaJasmine,這裏咱們選擇Mocha,斷言庫有shouldexpectchai以及node自帶的assert。這裏咱們選擇chai,chai中包含了expect、should及assert的書寫風格。node

npm install mocha chai --save-dev
  • index.js
const getNum = (value) => {
  return value * 2
}

module.exports = getNum
  • test.js
const chai = require('chai')
const expect = chai.expect
const getNum = require('../index')

describe('Test', function() {
  it('should return 20 when the value is 10', function() {
      expect(getNum(10)).to.equal(20)
  })
})

describe用於給測試用例分組,it表明一個測試用例。webpack

  • package.json
"scripts": {
  "test": "mocha"
}

 須要在全局下安裝Mochagit

npm install mocha -g

項目目錄下執行es6

npm run test

測試經過
github

完成代碼測試以後咱們再去看看代碼測試的覆蓋率。測試代碼覆蓋率咱們選擇使用istanbul,全局安裝web

npm install -g istanbul

使用istanbul啓動Mochachrome

istanbul cover _mocha

測試經過,覆蓋率100%

行覆蓋率(line coverage):是否每一行都執行了?
函數覆蓋率(function coverage):是否每一個函數都調用了?
分支覆蓋率(branch coverage):是否每一個if代碼塊都執行了?
語句覆蓋率(statement coverage):是否每一個語句都執行了?

修改index.js再看代碼覆蓋率

const getNum = (value) => {
  if(value === 0) {
    return 1
  }else {
    return value * 2
  }
}

module.exports = getNum

發現代碼覆蓋率發生了變化

修改test.js添加測試用例

describe('Test', function() {
  it('should return 20 when the value is 10', function() {
      expect(getNum(10)).to.equal(20)
  })
  it('should return 1 when the value is 0', function() {
    expect(getNum(0)).to.equal(0)
  })
})

代碼覆蓋率又回到了100%

客戶端代碼

客戶端代碼即運行在瀏覽器中的代碼,代碼中包含了window、document等對象,須要在瀏覽器環境下才能起做用。仍是以一個項目爲例,代碼結構以下:

.
├── index.js
├── node_modules
├── package.json
└── test
    └── test.js
    └── test.html

咱們依然使用Mocha測試庫及chai斷言庫。

npm install mocha chai --save-dev
  • index.js
window.createDiv = function(value) {
  var oDiv = document.createElement('div')
  oDiv.id = 'myDiv'
  oDiv.innerHTML = value
  document.body.appendChild(oDiv)
}
  • test.js
mocha.ui('bdd')

var expect = chai.expect
describe("Tests", function () {
  before(function () {
    createDiv('test')
  })
  it("content right", function () {
    var el = document.querySelector('#myDiv')
    expect(el).to.not.equal(null)
    expect(el.innerHTML).to.equal("test")
  })
})

mocha.run()
  • test.html
<html>
   <head>
     <title> Tests </title>
     <link rel="stylesheet" href="../node_modules/mocha/mocha.css"/>
   </head>
   <body>
     <div id="mocha"></div>
     <script src="../node_modules/mocha/mocha.js"></script>
     <script src="../node_modules/chai/chai.js"></script>
     <script src="../index.js"></script>
     <script src="./test.js"></script>
   </body>
 </html>

直接用瀏覽器打開test.html文件便能看到測試結果
2018-01-11 2 15 12

固然咱們能夠選擇PhantomJS模擬瀏覽器去作測試,這裏咱們使用mocha-phantomjs對test.html作測試。

全局安裝mocha-phantomjs

npm install mocha-phantomjs -g

修改package.json

"scripts": {
  "test": "mocha-phantomjs test/test.html"
}

項目目錄下執行

npm run test

mocha-phantomjs在mac下執行會報phantomjs terminated with signal SIGSEGV,暫時沒有找到什麼解決方案。因此我在ubuntu下執行,結果顯示如圖

2018-01-11 2 31 45

上述方式雖然能完成代碼的單元測試,可是要完成代碼覆蓋率的計算也沒有什麼好的方式,因此我選擇引入測試管理工具karma

karma使用指南

略去一大堆介紹karma的廢話,項目下引入karma

npm install karma --save-dev

初始化配置karma配置文件

npm install karma -g
karma init

使用karma默認配置看看每一項的做用

  • Karma.conf.js
module.exports = function(config) {
  config.set({
    basePath: '', // 設置根目錄
    frameworks: ['jasmine'], // 測試框架
    files: [ // 瀏覽器中加載的文件
    ],
    exclude: [ // 瀏覽器中加載的文件中排除的文件
    ],
    preprocessors: { // 預處理
    },
    reporters: ['progress'], // 添加額外的插件
    port: 9876, // 開啓測試服務時監聽的端口
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true, // 監聽文件變化,發生變化則從新編譯
    browsers: ['Chrome'], // 測試的瀏覽器
    singleRun: false, // 執行測試用例後是否關閉測試服務
    concurrency: Infinity
  })
}

此時的項目結構以下所示

.
├── index.js
├── node_modules
├── package.json
├── karma.conf.js
└── test
    └── test.js

首先咱們將測試框架jasmine改成咱們熟悉的mocha及chai,添加files及plugins

npm install karma karma-mocha karma-chai mocha chai karma-chrome-launcher --save-dev
  • karma.conf.js
module.exports = function(config) {
  config.set({
    basePath: '',
    frameworks: ['mocha', 'chai'],
    files: [
        'index.js',
        'test/*.js'
    ],
    exclude: [
    ],
    preprocessors: {
    },
    reporters: ['progress'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['Chrome'],
    singleRun: false,
    concurrency: Infinity,
    plugins: [
      'karma-chrome-launcher',
      'karma-mocha',
      'karma-chai',
    ]
  })
}

全局安裝karma-cli

npm install -g karma-cli

修改package.json

"scripts": {
  "test": "karma start karma.conf.js"
}

執行npm run test即可以啓用chrome去加載頁面。

karma測試代碼覆蓋率

測試代碼覆蓋率,咱們仍是選擇使用PhantomJS模擬瀏覽器,將singleRun設爲true,即執行完測試用例就退出測試服務。

npm install karma-phantomjs-launcher --save-dev

browsers中的Chrome改成PhantomJSplugins中的karma-chrome-launcher改成karma-phantomjs-launcher

執行npm run test ,測試經過且自動退出如圖所示

引入karma代碼覆蓋率模塊karma-coverage,改模塊依賴於istanbul

npm install istanbul karma-coverage --save-dev

修改karma.conf.js

module.exports = function(config) {
  config.set({
    basePath: '',
    frameworks: ['mocha', 'chai'],
    files: [
        'index.js',
        'test/*.js'
    ],
    exclude: [
    ],
    preprocessors: {
        'index.js': ['coverage']
    },
    reporters: ['progress', 'coverage'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['PhantomJS'],
    singleRun: true,
    concurrency: Infinity,
    coverageReporter: {
      type : 'text-summary'
    },
    plugins: [
      'karma-phantomjs-launcher',
      'karma-mocha',
      'karma-coverage',
      'karma-chai',
    ]
  })
}

對index.js文件使用coverage進行預處理,加入karma-coverage插件,覆蓋率測試輸出coverageReporter配置,詳見這裏這裏爲了方便截圖顯示將其設爲text-summary

執行npm run test,顯示結果以下圖所示

ES6代碼覆蓋率計算

目前的瀏覽器並不能兼容全部ES6代碼,因此ES6代碼都須要通過babel編譯後纔可在瀏覽器環境中運行,但編譯後的代碼webpack會加入許多其餘的模塊,對編譯後的代碼作測試覆蓋率就沒什麼意義了。

  • index.js
const createDiv = value => {
  var oDiv = document.createElement('div')
  oDiv.id = 'myDiv'
  oDiv.innerHTML = value
  document.body.appendChild(oDiv)
}

module.exports = createDiv

咱們使用ES6中的箭頭函數

  • test.js
const createDiv = require('../index')
describe("Tests", function () {
  before(function () {
    createDiv('test')
  })
  it("content right", function () {
    var el = document.querySelector('#myDiv')
    expect(el).to.not.equal(null)
    expect(el.innerHTML).to.equal("test")
  })
})
  • kama.conf.js
module.exports = function(config) {
  config.set({
    basePath: '',
    frameworks: ['mocha', 'chai'],
    files: [
        'test/*.js'
    ],
    exclude: [
    ],
    preprocessors: {
        'index.js': ['coverage'],
        'test/*.js': ['webpack']
    },
    reporters: ['progress', 'coverage'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['PhantomJS'],
    singleRun: true,
    concurrency: Infinity,
    coverageReporter: {
      type : 'text-summary'
    },
    webpack: {
      module: {
        rules: [
          {
            test: /\.js$/,
            exclude: /node_modules/,
            use: {
              loader: 'babel-loader',
              options: {
                "presets": ["es2015"],
                "plugins": [["istanbul"]]
              }
            }
          }
        ]
      }
    },
    plugins: [
      'karma-phantomjs-launcher',
      'karma-mocha',
      'karma-coverage',
      'karma-webpack',
      'karma-chai',
    ]
  })
}

test.js文件經過require引入index.js文件,因此files只需引入test.js文件,再對test.js作webpack預處理。

須要相關的babel,webpack,karma依賴以下:

"devDependencies": {
  "babel-core": "^6.26.0",
  "babel-loader": "^7.1.2",
  "babel-plugin-istanbul": "^4.1.5",
  "babel-preset-es2015": "^6.24.1",
  "chai": "^4.1.2",
  "istanbul": "^0.4.5",
  "karma": "^2.0.0",
  "karma-chai": "^0.1.0",
  "karma-coverage": "^1.1.1",
  "karma-mocha": "^1.3.0",
  "karma-phantomjs-launcher": "^1.0.4",
  "karma-webpack": "^2.0.9",
  "mocha": "^4.1.0",
  "webpack": "^3.10.0"
}

執行npm run dev顯示如圖所示

travisCI及coveralls

travisCI的配置這裏不作詳解,咱們將經過代碼測試覆蓋率上傳到coveralls獲取一個covarage的icon。

2018-01-11 6 29 18

若是你是服務端代碼使用istanbul計算代碼覆蓋率的

  • .travis.yml
language: node_js
node_js:
- 'stable'
- 8
branches:
  only:
  - master
install:
- npm install
script:
- npm test
after_script: "npm install coveralls && cat ./coverage/lcov.info | coveralls"

若是你是服務端代碼使用karma計算代碼覆蓋率的,則需使用coveralls模塊

npm install coveralls karma-coveralls --save-dev
  • Karma.conf.js
// Karma configuration
module.exports = function(config) {
  config.set({
    basePath: '',
    frameworks: ['mocha', 'chai'],
    files: [
      'test/*.js'
    ],
    exclude: [],
    preprocessors: {
      'test/*.js': ['webpack'],
      'index.js': ['coverage']
    },
    reporters: ['progress', 'coverage', 'coveralls'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['PhantomJS'],
    singleRun: true,
    concurrency: Infinity,
    webpack: {
      module: {
        rules: [
          {
            test: /\.js$/,
            exclude: /node_modules/,
            use: {
              loader: 'babel-loader',
              options: {
                "presets": ["es2015"],
                "plugins": [["istanbul"], ["transform-runtime"]]
              }
            }
          }
        ]
      }
    },
    coverageReporter: {
      type : 'lcov',
      dir : 'coverage/'
    },
    plugins: [
      'karma-webpack',
      'karma-phantomjs-launcher',
      'karma-coverage',
      'karma-mocha',
      'karma-chai',
      'karma-coveralls'
    ],
  })
}

plugins添加karma-coveralls,reporters添加coveralls,coverageReporter輸出配置改成lcov。

能夠參考我本身實現的一個show-toast

參考

https://github.com/tmallfe/tm...
https://codeutopia.net/blog/2...
https://github.com/jdavis/tdd...
https://jasmine.github.io/
https://github.com/bbraithwai...
https://mochajs.org/
https://toutiao.io/posts/5649...
https://coveralls.io/
https://karma-runner.github.i...
https://github.com/karma-runn...
https://shouldjs.github.io/
https://juejin.im/post/598073...
http://www.bradoncode.com/blo...
http://docs.casperjs.org/en/l...
https://github.com/gotwarlost...
https://www.jianshu.com/p/ffd...
http://www.bijishequ.com/deta...
http://phantomjs.org/
https://github.com/CurtisHump...
http://www.jackpu.com/shi-yon...
https://github.com/JackPu/Jav...
https://github.com/caitp/karm...
https://github.com/karma-runn...

相關文章
相關標籤/搜索