Vue項目的自動化測試

Vue項目的自動化測試

說到自動化測試,許多開發團隊都是據說過、嘗試過,但最後都止步於嘗試,不能將TDD(測試驅動開發)、BDD(行爲驅動開發)的完整流程貫徹到項目中。思考其中的緣由:終究仍是成本抵不上收益。javascript

不少後端開發人員可能寫過不少自動化的單元測試代碼,可是對前端測試一頭霧水。這是由於相對於後端開發人員的自動化單元測試,前端的自動化測試成本更高。css

自動化測試就是經過自動化腳本將一個又一個測試用例串起來,每一個測試用例都要模擬環境、模擬輸入、而後斷言輸出。前端自動化最難的地方就是模擬環境、模擬輸入和斷言輸出了!
咱們能夠試想一下現實中的使用場景:html

模擬環境:首先前端代碼是跑在不一樣的終端環境上的,純粹的使用某臺機子的運行環境進行模擬是沒法發現真正存在的問題。因此咱們的測試用例必須跑在真實的環境下,這裏麪包括不一樣的機器:Android、ios、pc、macbook;不一樣的系統:window十、window八、linux、mac;不一樣的運行載體:IE、safari、chrome、firefox、Opera、Android webview、UIWebview、WKWebview;不一樣網絡環境:WiFi、4G、3G、offline前端

模擬輸入:前端的輸入很差模擬,在PC上有鼠標click,double Click、drag、mouseDown、mouseOver、input等等,在mobile上有swipe、tap、scroll、搖一搖、屏幕翻轉等。相對於後端的單元測試,前端的輸入種類繁多,每一種模擬起來都十分複雜,並且不少bug隱藏在幾種連貫的輸入以後纔會復現。vue

斷言輸出:前端的斷言不是簡單的判斷值是否相等,不少狀況是即便值相等、效果徹底不同。
不少展現效果更是不能經過簡單的斷言來檢測,好比區域是否能滑動,輸入時鍵盤是否正確彈起等。java

當你跨越千山萬水把上面的問題解決了,測試用例寫好了,功能代碼寫好,完美!而後UE跑過來和你說那個這根線往左邊移動一像素的時候,你會瞬間崩潰。可能這一像素你不少測試用例都得重寫。因此前端自動化測試的成本真不必定抵得上收益。node

可是困難不表明解決不了,部分場景不適合不表明全部場景都不適合!linux

正由於面臨這麼多的困難,咱們的前端社區開發出了不少工具幫咱們解決這些問題。本章節主要是結合Vue這個框架介紹前端自動化測試的一些工具和方法。webpack

咱們使用vue-cli去新建一個vue的新項目,在這個項目中開啓默認的unit tests和e2e testsios

bogon:work xiaorenhui$ vue init webpack vueExample

? Project name vue-example
? Project description A Vue.js project
? Author kukuv <kukuv>
? Vue build standalone
? Install vue-router? No
? Use ESLint to lint your code? No
? Setup unit tests with Karma + Mocha? Yes
? Setup e2e tests with Nightwatch? Yes

下面列舉下這個新項目中涉及到的一些開源項目:

  • karma

    • Karma是一個基於Node.js的JavaScript測試執行過程管理工具(Test Runner)。該工具可用於測試全部主流Web瀏覽器,也可集成到CI(Continuous integration)工具,也可和其餘代碼編輯器一塊兒使用。這個測試工具的一個強大特性就是,它能夠監控(Watch)文件的變化,而後自行執行,經過console.log顯示測試結果。
  • Mocha

    • mocha是一款功能豐富的javascript單元測試框架,它既能夠運行在nodejs環境中,也能夠運行在瀏覽器環境中。
  • Nightwatch

    • Nightwatch是一套基於Node.js的測試框架,使用Selenium WebDriver API以將Web應用測試自動化。它提供了簡單的語法,支持使用JavaScript和CSS選擇器,來編寫運行在Selenium服務器上的端到端測試。
  • phantomjs

    • 一個基於webkit內核的無頭瀏覽器,即沒有UI界面,即它就是一個瀏覽器,只是其內的點擊、翻頁等人爲相關操做須要程序設計實現。
  • sinon-chai

    • sinon-chai是 sinon和chai這兩個斷言庫的結合,提供豐富的斷言方法

不少人看到這麼多新名詞必定頭暈,心想一個單元測試咋須要懂這麼多東西。狀況是上面只是單元測試框架的一小部分、還有許多框架沒有列出來。正由於前端的自動化測試面臨着許多問題,因此咱們纔有這麼多的框架來幫忙解決問題。

unit tests

咱們先來分析一下這個項目中的unit tests,這裏面用到了 Karma、Mocha、sinon-chai、phantomjs。項目中已經有一個默認的單元測試例子。karma做爲測試執行過程管理工具把Mocha、sinon-chai、phantomjs等框架組織起來。Mocha用來描述測試用例、sinon-chai用來斷言、而後使用phamtomjs做爲運行環境來跑測試用例。

npm install 將依賴的庫都安裝好,這裏面phantomjs的依賴會比較難裝,若是你之間沒有安裝過phantom,由於phantom比較大,並且加上國內的網絡環境等緣由。若是phantomjs裝不上能夠嘗試使用chrome做爲運行環境,這須要安裝 "karma-chrome-launcher",須要修改配置文件。

而後 npm run unit 跑一下unit tests,若是提示權限問題就 使用sudo 來提高下權限。跑完後咱們看一下目錄結構

└── unit
    ├── coverage  代碼覆蓋率報告,src下面的index.html能夠直接用瀏覽器打開
    │   ├── lcov-report
    │   │   ├── base.css
    │   │   ├── index.html
    │   │   ├── prettify.css
    │   │   ├── prettify.js
    │   │   ├── sort-arrow-sprite.png
    │   │   ├── sorter.js
    │   │   └── src
    │   │       ├── App.vue.html
    │   │       ├── components
    │   │       │   ├── Hello.vue.html
    │   │       │   └── index.html
    │   │       └── index.html
    │   └── lcov.info
    ├── index.js 運行測試用例前先加載的文件,方便統計代碼覆蓋率
    ├── karma.conf.js karma的配置文件
    └── specs 全部的測試用例都放在這裏
        └── Hello.spec.js
// 加載全部的測試用例、 testsContext.keys().forEach(testsContext)這種寫法是webpack中的加載目錄下全部文件的寫法

const testsContext = require.context('./specs', true, /\.spec$/)
testsContext.keys().forEach(testsContext)

// 加載全部代碼文件,方便統計代碼覆蓋率
const srcContext = require.context('../../src', true, /^\.\/(?!main(\.js)?$)/)
srcContext.keys().forEach(srcContext)
config.set({
    // 在幾個環境裏跑你的測試用例
    // browsers: ['PhantomJS','Chrome'], 
    browsers: ['Chrome'],
    // 默認加載幾個框架
    frameworks: ['mocha', 'sinon-chai', 'phantomjs-shim'],
    // 使用那些彙報框架
    reporters: ['spec', 'coverage'],
    // 預加載文件
    files: ['./index.js'],
    // 預處理
    preprocessors: {
      './index.js': ['webpack', 'sourcemap']
    },
    // webpack 配置
    webpack: webpackConfig,
    webpackMiddleware: {
      noInfo: true
    },
    // coverage 配置
    coverageReporter: {
      dir: './coverage',
      reporters: [
        { type: 'lcov', subdir: '.' },
        { type: 'text-summary' }
      ]
    }
  })

上面使用的插件例如 mocha、spec、coverage除了karma默認自帶的都須要你在npm
上安裝對應的插件,例如如下

"karma": "^1.4.1",
    "karma-chrome-launcher": "^2.2.0",
    "karma-coverage": "^1.1.1",
    "karma-mocha": "^1.3.0",
    "karma-phantomjs-launcher": "^1.0.2",
    "karma-phantomjs-shim": "^1.4.0",
    "karma-sinon-chai": "^1.3.1",
    "karma-sourcemap-loader": "^0.3.7",
    "karma-spec-reporter": "0.0.31",
    "karma-webpack": "^2.0.2",
> vue-exampl@1.0.0 unit /Users/xiaorenhui/work/vueExample
> cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run

07 09 2017 12:08:13.004:INFO [karma]: Karma v1.7.0 server started at http://0.0.0.0:9876/
07 09 2017 12:08:13.007:INFO [launcher]: Launching browser Chrome with unlimited concurrency
07 09 2017 12:08:13.015:INFO [launcher]: Starting browser Chrome
07 09 2017 12:08:15.475:INFO [Chrome 60.0.3112 (Mac OS X 10.12.3)]: Connected on socket qDaxr51TuQCfQBcVAAAA with id 73077049
INFO LOG: 'Download the Vue Devtools extension for a better development experience:
https://github.com/vuejs/vue-devtools'
LOG LOG: 'data'

  Hello.vue
    ✓ should render correct contents

Chrome 60.0.3112 (Mac OS X 10.12.3): Executed 1 of 1 SUCCESS (0.024 secs / 0.011 secs)
TOTAL: 1 SUCCESS


=============================== Coverage summary ===============================
Statements   : 60% ( 3/5 )
Branches     : 50% ( 1/2 )
Functions    : 0% ( 0/1 )
Lines        : 60% ( 3/5 )
================================================================================

我修改了一下Hello.vue這個組件,能夠看到coverage 裏精確的顯示了測試代碼的覆蓋率,下面是我作的修改

export default {
  name: 'hello',
  data () {
      console.log('data');
      function aa() {

      }
      if(false){
          console.log('data aa');
      }
    return {
      msg: 'Welcome to Your Vue.js App'
    }
  },
  methods:{
      aa(){
          console.log('methods aa');
      }
  }
}


打開reporter下面的index.html咱們能夠看到代碼覆蓋的具體狀況。
點開Hello.vue更有直觀的方式展現哪些代碼被覆蓋了,哪些沒有。
圖片描述

e2e測試

既然咱們已經有了單元測試,那e2e測試有和單元測試有什麼區別呢?Nightwatch是前端e2e測試的一個有表明性的框架。單元測試TDD的粒度很細,咱們會爲許多函數、方法去寫單元測試,而e2e更接近BDD。直白點說,就是TDD的測試單元是一個個函數、方法,而BDD測試的單元是一個個預期的行爲表現。e2e作的事情就是打開瀏覽器,而且真正的訪問咱們最終的頁面,而後在這個真實的瀏覽器、真實的頁面中咱們去作各類斷言,而單元測試不會要去咱們去訪問最終的頁面,單元測試要保證的是一個個單元是沒有問題的,但這些單元組合起來跑在頁面上是否有問題,不是單元測試可以保證的,尤爲是在前端這種模擬環境、模擬輸入很是複雜的領域中,這是單元測試的短板,而e2e測試就是用來解決這些短板的。

咱們來看看項目中使用Nightwatch來進行e2e測試的例子

首先看一下目錄

├── e2e
│   ├── custom-assertions
│   │   └── elementCount.js 自定義的斷言方法
│   ├── nightwatch.conf.js nightwatch的配置文件
│   ├── reports 
│   │   ├── CHROME_60.0.3112.101_Mac\ OS\ X_test.xml
│   │   └── CHROME_60.0.3112.113_Mac\ OS\ X_test.xml
│   ├── runner.js  bootstrap文件,起咱們的頁面server和nightwatch文件
│   └── specs
│       └── test.js 測試用例

圖片描述
selenium是一個用java寫的e2e測試工具集,它的API被歸入 w3c的webDriver Api中, nightWatch是對selenium的一個nodejs封裝。全部咱們須要再配置文件中配置selenium。

src_folders: ['test/e2e/specs'],
  output_folder: 'test/e2e/reports',
  custom_assertions_path: ['test/e2e/custom-assertions'],
    // 對selenium的配置
  selenium: {
    start_process: true,
    server_path: require('selenium-server').path,
    host: '127.0.0.1',
    port: 4444,
    cli_args: {
      'webdriver.chrome.driver': require('chromedriver').path
    }
  },
    // 測試環境的配置
  test_settings: {
    default: {
      selenium_port: 4444,
      selenium_host: 'localhost',
      silent: true,
      globals: {
        devServerURL: 'http://localhost:' + (process.env.PORT || config.dev.port)
      }
    },

    chrome: {
      desiredCapabilities: {
        browserName: 'chrome',
        javascriptEnabled: true,
        acceptSslCerts: true
      }
    },

    firefox: {
      desiredCapabilities: {
        browserName: 'firefox',
        javascriptEnabled: true,
        acceptSslCerts: true
      }
    }
  }

下面的runner須要先起一個咱們的網頁服務而後再起nightWatch服務

var server = require('../../build/dev-server.js')

server.ready.then(() => {
  // 2. run the nightwatch test suite against it
  // to run in additional browsers:
  //    1. add an entry in test/e2e/nightwatch.conf.json under "test_settings"
  //    2. add it to the --env flag below
  // or override the environment flag, for example: `npm run e2e -- --env chrome,firefox`
  // For more information on Nightwatch's config file, see
  // http://nightwatchjs.org/guide#settings-file
  var opts = process.argv.slice(2)
    console.log(opts);
  if (opts.indexOf('--config') === -1) {
    opts = opts.concat(['--config', 'test/e2e/nightwatch.conf.js'])
  }
  if (opts.indexOf('--env') === -1) {
    opts = opts.concat(['--env', 'chrome,firefox'])
  }

  var spawn = require('cross-spawn')
  var runner = spawn('./node_modules/.bin/nightwatch', opts, { stdio: 'inherit' })

  runner.on('exit', function (code) {
    server.close()
    process.exit(code)
  })

  runner.on('error', function (err) {
    server.close()
    throw err
  })
})

sudo npm run e2e後

> node test/e2e/runner.js

> Starting dev server...

Starting to optimize CSS...
> Listening at http://localhost:8080

[]
Starting selenium server... started - PID:  74459

[Test] Test Suite
=====================

Running:  default e2e tests
 ✔ Element <#app> was visible after 81 milliseconds.
 ✔ Testing if element <.hello> is present.
 ✔ Testing if element <h1> contains text: "Welcome to Your Vue.js App".
 ✔ Testing if element <img> has count: 1

OK. 4 assertions passed. (3.951s)

在控制檯上咱們能看到各類斷言的結果

相關文章
相關標籤/搜索