爲解放勞動力,發展生產力css
測試有了這般變化:
鼠標點擊手動測試 -> 用腳本模擬,自動化測試html
配置主要是兩個,一是 karma 的配置文件,另外一個是 karma 須要的webpack 配置文件。vue
webpack 的配置文件是爲了解析那些須要測試的源文件(vue 相關的文件),而後再給karma 的單元測試用例去識別。node
var path = require("path") var webpack = require("webpack") var ExtractTextPlugin = require('extract-text-webpack-plugin') function resolve(dir) { return path.join(__dirname, '..', dir) } var webpackConfig = { module: { rules: [ // babel-loader { test: /\.js$/, use: 'babel-loader', include: [resolve('src'), resolve('test')] }, // 爲了統計代碼覆蓋率,對 js 文件加入 istanbul-instrumenter-loader { test: /\.(js)$/, exclude: /node_modules/, include: /src|packages/, enforce: 'post', use: [{ loader: "istanbul-instrumenter-loader", options: { esModules: true }, }] }, // vue loader { test: /\.vue$/, use: [{ loader: 'vue-loader', options: { // 爲了統計代碼覆蓋率,對 vue 文件加入 istanbul-instrumenter-loader preLoaders: { js: 'istanbul-instrumenter-loader?esModules=true' } } }] }, // css loader { test: /\.css$/, use: ExtractTextPlugin.extract({ use: 'css-loader', fallback: 'vue-style-loader' }) }, // img loader { test: /\.(png|gif|jpe?g)(\?\S*)?$/, use: [{loader: 'url-loader'}] }, // font loader { test: /\.(eot|woff|woff2|ttf|svg)(\?\S*)?$/, use: [{loader: 'url-loader'}] }, ] }, resolve: { extensions: ['.js', '.vue', '.json'], alias: { 'vue$': 'vue/dist/vue.esm.js', '@': resolve('src'), // 調用組件的時候方便點 } }, plugins: [ new webpack.DefinePlugin({ 'process.env': { NODE_ENV: '"production"' } }) ] } module.exports = webpackConfig
有兩種方法:webpack
karma init
命令,而後一系列提示選擇...karma init
命令,新建一個karma.conf.js
文件,而後配置:var webpackConfig = require('../../build/webpack.test.config'); 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'], frameworks: ['mocha', 'sinon-chai', 'phantomjs-shim'], reporters: ['spec', 'coverage'], files: ['index.js'], preprocessors: { './index.js': ['webpack', 'sourcemap'] }, webpackMiddleware: { noInfo: true }, // 不顯示 `webpack` 打包日誌信息 webpackServer: { noInfo: true }, webpack: webpackConfig, coverageReporter: { dir: './coverage', reporters: [ { type: 'lcov', subdir: '.' }, { type: 'text-summary' } ] } }) }
├─build │ webpack.test.config.js │ ├─src │ ├─package.json │ └─test └─unit │ index.js │ karma.config.js │ ├─coverage │ └─specs *.spec.js
測試文件相關都放置在 test/unit 下,入口文件爲 index.js,每一個vue 組件對應的測試用例名爲組件名稱.spec.js,根據 istanbul-instrumenter-loader 文檔的說明,測試總入口文件 index.js 內容以下:git
import Vue from 'vue' Vue.config.productionTip = false // 測試全部以 .spec.js 名稱結尾的文件 // require all test files (files that ends with .spec.js) const testsContext = require.context('./specs', true, /\.spec$/) testsContext.keys().forEach(testsContext) // 要求除main.js以外的全部src文件進行覆蓋 // 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)
"devDependencies": { ... "babel-core": "^6.9.0", "babel-loader": "^7.1.1", "babel-plugin-syntax-jsx": "^6.18.0", "babel-plugin-transform-runtime": "^6.23.0", "babel-plugin-transform-vue-jsx": "^3.3.0", "babel-polyfill": "^6.26.0", "babel-preset-env": "^1.6.0", "babel-preset-es2015": "^6.18.0", "babel-preset-stage-2": "^6.18.0", "babel-runtime": "^6.18.0", "chai": "^4.1.2", "chalk": "^2.0.1", "css-loader": "^0.28.4", "extract-text-webpack-plugin": "^2.1.2", "istanbul-instrumenter-loader": "^3.0.0", "karma": "^1.7.1", "karma-coverage": "^1.1.1", "karma-mocha": "^1.3.0", "karma-phantomjs-launcher": "^1.0.4", "karma-phantomjs-shim": "^1.4.0", "karma-sinon-chai": "^1.3.2", "karma-sourcemap-loader": "^0.3.7", "karma-spec-reporter": "0.0.31", "karma-webpack": "^2.0.4", "mocha": "^3.5.0", "phantomjs-prebuilt": "^2.1.15", "postcss-px2rem": "^0.3.0", "sinon": "^3.2.1", "sinon-chai": "^2.13.0", ... "style-loader": "^0.18.2", "url-loader": "^0.5.7", "vue-loader": "^13.0.4", "vue-router": "^2.7.0" },
執行命令 sudo npm install
安裝所須要的包github
注意:web
當流程走到下面這個步驟時,可能耗費的時間比較長,千萬別ctrl + c
中斷下載從新install,靜靜等待就好vue-router
新建一個Hello.vue
組件npm
<template> <div>msg</div> </template> <script> export default { name: 'hello', data () { return { msg: 'Welcome to Your Vue.js App' } } } </script>
在test/unit/specs
目錄下建立一個 Hello.spec.js
文件,再寫個簡單的單元測試用例:
import Vue from 'vue' import Hello from '@/components/Hello' describe('Hello.vue', () => { it('should render correct contents', () => { const Constructor = Vue.extend(Hello) const vm = new Constructor().$mount() expect(vm.$el.querySelector('.hello h1').textContent) .to.equal('Welcome to Your Vue.js App') }) })
在package.json
文件配置:
// package.json "scripts": { "test": "karma start test/unit/karma.config.js --single-run" }
npm run test
輸出測試結果,同時在 test/unit/coverage
生成測試報告。網上的例子...
Jest:JS單元測試工具,包含DOM API 支持、斷言庫、Mock 庫等,還包含了 Spapshot Testing、 Instant Feedback 等特性
Enzyme:經過 jQuery 風格的方式進行DOM 處理,開發體驗十分友好
npm install jest enzyme babel-jest --save-dev
"jest": { "moduleFileExtensions": [ "js", "jsx" ], "moduleNameMapper": { "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js", ".*\\.(css|less|scss)$": "<rootDir>/__mocks__/styleMock.js" }, "transform": { "^.+\\.js$": "babel-jest" } },
"scripts": { "test": "jest" }
import { shallow } from 'enzyme'
Enzyme 提供了三種方法
通常狀況下,shallow 就已經足夠用了,偶爾狀況下會用到 mount。
const setup = () => { // 模擬 props const props = { // Jest 提供的mock 函數 onAddClick: jest.fn() } // 經過 enzyme 提供的 shallow(淺渲染) 建立組件 const wrapper = shallow(<AddTodoView {...props} />) return { props, wrapper } }
Case1::測試組件是否正常渲染
describe('AddTodoView', () => { const { wrapper, props } = setup(); // case1 // 經過查找存在 Input,測試組件正常渲染 it('AddTodoView Component should be render', () => { //.find(selector) 是 Enzyme shallow Rendering 提供的語法, 用於查找節點 // 詳細用法見 Enzyme 文檔 http://airbnb.io/enzyme/docs/api/shallow.html expect(wrapper.find('input').exists()); }) })
Case2:輸入內容並敲下回車鍵,測試組件調用props的方法
it('When the Enter key was pressed, onAddClick() shoule be called', () => { // mock input 輸入和 Enter事件 const mockEvent = { keyCode: 13, // enter 事件 target: { value: 'Test' } } // 經過 Enzyme 提供的 simulate api 模擬 DOM 事件 wrapper.find('input').simulate('keyup',mockEvent) // 判斷 props.onAddClick 是否被調用 expect(props.onAddClick).toBeCalled() })
Jest 還提供了生成測試覆蓋率報告的命令,只須要添加上 --coverage
這個參數既可生成
"scripts": { "coverage": "jest --colors --coverage" }
難點是要考慮周全,測試用例覆蓋全面