为解放劳动力,发展生产力
测试有了这般变化:
鼠标点击手动测试 -> 用脚本模拟,自动化测试
Vue中的组件测试
需要安装的包
- 全局安装:babel、mocha、karma
- 其他局部安装的包在下面的【测试环境搭建】最下方配置文件中给出
测试环境搭建
配置主要是两个,一是 karma 的配置文件,另一个是 karma 需要的webpack 配置文件。
webpack 的配置文件是为了解析那些需要测试的源文件(vue 相关的文件),然后再给karma 的单元测试用例去识别。
webpack3 配置文件
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
karma配置文件
有两种方法:
- cd进入当前项目,使用
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 内容如下:
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)
最后看下package.json的devDependencies
"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
安装所需要的包
注意:
当流程走到下面这个步骤时,可能耗费的时间比较长,千万别ctrl + c
中断下载重新install,静静等待就好
开始测试
-
新建一个
Hello.vue
组件<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
生成测试报告。
一些测试的Case
网上的例子...
React中的组件测试
Jest + Enzyme
Jest:JS单元测试工具,包含DOM API 支持、断言库、Mock 库等,还包含了 Spapshot Testing、 Instant Feedback 等特性
Enzyme:通过 jQuery 风格的方式进行DOM 处理,开发体验十分友好
测试环境搭建
1. 安装一些包
npm install jest enzyme babel-jest --save-dev
2. 在package.json中配置jest:
"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"
}
},
3. 新增启动测试的脚本:
"scripts": {
"test": "jest"
}
开始测试
1. 引入
import { shallow } from 'enzyme'
Enzyme 提供了三种方法
- shallow:浅渲染。其作用是仅仅渲染至虚拟节点,不会返回真实的节点,能极大提高测试性能。但是它不适合测试包含子组件、需要测试声明周期的组件
- mount:Full Rendering,非常适用于存在于 DOM API 存在交互组件,或者需要测试组件完整的生命周期
- render:Static Rendering,用于 将 React 组件渲染成静态的 HTML 并分析生成的 HTML 结构
一般情况下,shallow 就已经足够用了,偶尔情况下会用到 mount。
2. 模拟
const setup = () => {
// 模拟 props
const props = {
// Jest 提供的mock 函数
onAddClick: jest.fn()
}
// 通过 enzyme 提供的 shallow(浅渲染) 创建组件
const wrapper = shallow(<AddTodoView {...props} />)
return {
props,
wrapper
}
}
3. 编写Test Case
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"
}
写在最后
难点是要考虑周全,测试用例覆盖全面
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。