代碼測試:項目穩健的有力保證

懶惰,是促令人類科技發展的重要因素。咱們告別刀耕火種的時代,正是由於人們不斷地經過發明工具和優化精簡手動的流程來實現效率的提高,讓人們能專一於本身專業的領域,其餘的事情交給機械去處理。

而一樣在前端的領域,咱們也是從蠻荒的時代走向現在的繁榮發達,各種框架百花齊放,前端也再也不是侷限於靜態頁面,形態更加接近於傳統應用的開發,功能的複雜度呈指數級的提高。有可能一個庫要同時知足多個項目使用,版本的迭代也會致使功能的變化,若是每次到這個時候,就要讓我從新測試一遍代碼,那我還能不能準時下班了……html

因此咱們須要在開發以前,就肯定這個框架/庫的使用規則,經過這個規則去寫咱們的測試用例,這樣咱們開發完以後,跑一遍測試用例,讓機器取代咱們人工手動地去測試代碼,就能夠知道咱們的框架在各類環境和使用狀況下會否報錯,而再也不須要逐一項目去驗證。前端


固然並非只有我有這種想法,大神們早就想到了,因而測試框架應運而生。
就筆者瞭解,目前前端領域比較流行的單元測試框架有 mochaJasmine 等等。。node

事不宜遲,咱們看看如何上手測試,這裏咱們以mocha舉例子。
首先要在項目中安裝mochagit

npm install mocha --save-dev

在項目的根目錄建立 test 文件夾 並建立文件 index.js。如今要測試 Array.prototype.indexOfgithub

var assert = require('assert')

describe('Array', function() {
  describe('#indexOf()', function() {
    it('should return -1 when the value is not present', function() {
      assert.equal(-1, [1, 2, 3].indexOf(4))
    })
  })
})

在命令行中執行web

mocha

若是看到這樣的結果,恭喜你,你的代碼已經經過了測試(滑稽)chrome

Array
    #indexOf()
      ✓ should return -1 when the value is not present


  1 passing (13ms)

以上是一個簡單例子,可能有不少同窗仍是會一臉懵逼,說好的講測試框架,你給引入一個assert的是什麼鬼?
這我就要解釋一下了,這個是nodejs中內置的斷言模塊npm

何謂斷言?api

在程序設計中,斷言(assertion)是一種放在程序中的一階邏輯(如一個結果爲真或是假的邏輯判斷式),目的是爲了標示與驗證程序開發者預期的結果-當程序運行到斷言的位置時,對應的斷言應該爲真。若斷言不爲真時,程序會停止運行,並給出錯誤消息。 -- 摘自維基百科

測試框架的職責,只是幫你對項目中各類各樣的測試來進行一個規劃,方便對測試來進行拆分整理,並在測試中添加一些描述來方便人眼判別,實際上,輸入參數到函數中執行,最終輸出什麼結果才能經過測試,是須要咱們去「告訴」測試框架的。而斷言,正好知足咱們這種行爲的語義,也是最天然的。promise

通常來講使用默認的斷言模塊已經足以應對多種狀況,固然若是你對這種語法使用表示很不爽的時候,也能夠在測試框架中選擇使用其餘斷言庫, 好比 should.js , chai 這裏就再也不過多地贅述。

如何測試異步代碼

你能夠在回調中執行done

var assert = require('assert')

describe('Array', function() {
  describe('#indexOf()', function() {
    it('should return -1 when the value is not present', function(done) {
      setTimeout(function() {
        var value = 1;
        if (value) done(assert.equal(-1, [1, 2, 3].indexOf(4)))
        else done()
      }, 30)
    })
  })
})

promise

it('should xxx', function(done) {
  return new Promise(function(resolve, reject){}).then(done)
})

支持 async await

it('should return -1 when the value is not present', async function(done) {
  let a = await func();
  if (a) done()
  else done(new Error("xxx"))
})

須要注意的是,在使用mocha api的時候並不提倡 箭頭函數的寫法,由於這樣你會拿不到當前執行的context, 當前context下內置了好比 timeOut 等等的一些api,詳情請翻閱文檔,這裏就不作過多展開了。


測試環境

是時候在瀏覽器跑一下了!

沒錯,截止到如今,咱們一直都是在nodejs的環境下執行代碼,至於瀏覽器的執行環境有多少差別,你們不言自明,簡簡單單在node上面跑一下測試時不具備普適性,不可靠的,咱們須要在瀏覽器環境都跑一遍。

什麼?你想打開控制檯粘帖代碼執行?爲了拯救你於無盡的加班測試中,是時候推薦你接入使用karma了。

karma 是一套測試執行管理工具,能夠配置須要測試的瀏覽器環境,以及測試前的一系列準備工做等,須要時還能夠接入測試覆蓋率的報告生成。

爲了下降同窗們配置karma的門檻,這裏我換用了斷言庫should.js
npm install should --save-dev

斷言寫法替換以下

// assert.equal([1,2,3].indexOf(4), -1)
[1,2,3].indexOf(4).should.equal(-1)

首先咱們須要安裝karma

npm install karma-cli -g
npm install karma --save-dev

進到項目中初始化karma

karma init

接下來你會收到一連串的詢問

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 any browsers 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".
Enter empty string to move to the next question.
> node_modules/should/should.js
> test/*.js

Should any of the files included by the previous patterns be excluded ?
You can use glob patterns, eg. "**/*.swp".
Enter empty string 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 文件,我很想展開,可是若是繼續講下去估計講到天亮,因此想深刻了解每一個配置項的做用能夠翻閱karma文檔

// Karma configuration
// Generated on Fri Aug 04 2017 20:53:38 GMT+0800 (CST)

module.exports = function(config) {
  let configuration = {

    // 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'],


    // list of files / patterns to load in the browser
    files: [
      'node_modules/should/should.js',
      'js/*.js',
      'quz/*.js',
      'test/*.js'
    ],


    // list of files to exclude
    exclude: [
    ],


    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
      'quz/*.js': 'coverage',
      'js/*.js': 'coverage'
    },


    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress', 'coverage', 'coveralls'],
    coverageReporter: {
      type : 'lcov',
      dir : 'coverage/'
    },

    // web server port
    port: 9876,
    plugins: [
      'karma-mocha',
      'karma-chrome-launcher',
      'karma-firefox-launcher',
      'karma-coverage',
      'karma-coveralls'
    ],

    // 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
    logLevel: config.LOG_INFO,


    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,


    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['ChromeHeadless', 'FirefoxHeadless'],

    customLaunchers: {
      Chrome_travis_ci: {
        base: "ChromeHeadless",
        flags: ["--no-sandbox"]
      },
      FirefoxHeadless: { 
        base: "Firefox", 
        flags: ["-headless"]
      }
    },

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: process.env.TRAVIS,

    // Concurrency level
    // how many browser should be started simultaneous
    concurrency: Infinity
  }
  if (process.env.TRAVIS) {
    configuration.browsers = ["Chrome_travis_ci", 'FirefoxHeadless'];
  }
  config.set(configuration)
}

接下來咱們跑一發

karma start

你的黑窗口會有這樣的日誌

02 02 2018 18:10:26.181:WARN [karma]: No captured browser, open http://localhost:9876/
02 02 2018 18:10:26.193:INFO [karma]: Karma v2.0.0 server started at http://0.0.0.0:9876/
02 02 2018 18:10:26.194:INFO [launcher]: Launching browser Chrome with unlimited concurrency
02 02 2018 18:10:26.199:INFO [launcher]: Starting browser Chrome
02 02 2018 18:10:27.346:INFO [Chrome 63.0.3239 (Mac OS X 10.13.2)]: Connected on socket lv0dmScnbh5jC2NgAAAA withid 45333497
Chrome 63.0.3239 (Mac OS X 10.13.2): Executed 1 of 1 SUCCESS (0.003 secs / 0.001 secs)

這時候你的電腦會自動開啓瀏覽器,而且常駐在桌面,karma一旦觀測到你的代碼發生變更,就會從新跑一次測試用例,今後告別繁複的測試環節,專心寫代碼。perfect!

完了麼?並無!你還能夠更懶,把當前測試流程接入CI,成爲部署環境前的一個環節,固然這已經超出了本文應該討論的範圍。

長路漫漫,本文僅限於你們做爲了解測試的開篇,實際上隱藏了不少的細節,真正的環境,其實碰到的問題比這個要複雜得多,可是若是懂得了測試的原理,相信聰明的你碰到再難的問題也能想到解決的辦法。

學海無涯,共勉之。

相關文章
相關標籤/搜索