以前由于上家公司的经营出了问题,年前的大裁人,过了一个漫长的春节。
以后加入了新公司,而后正好遇上一个很紧急的项目,忙成狗,所以很久没更新文章了。
不过,我又回来啦!javascript
自动化测试,咱们将使用karma和nightmare,内容会包括:html
其实,单元测试通常用在写公共包的时候,好比通用的js函数库,通用的UI组件库。基本不太会在作业务项目的时候还使用单元测试。
而后,e2e测试的话,那其实每每是测试工程师须要作的,每每使用selenium。
那难道前端就不须要学测试了吗?
答案确定是否认的,否则我写个毛......
vue的项目就用了单元测试和e2e。
基于vue的UI组件库,好比:饿了么的element、滴滴的cube-ui、有赞的vant等都有单元测试(咩有e2e,由于不必)。
滴滴的话,我问了下黄轶大佬,他们项目前端自动化测试是用了单元测试和e2e的。
总之,两种都是由应用场景的,e2e虽然用的很少,或者说有时候不是前端自动化的范畴,可是其实作起来很简单,学会准没错!前端
npm install karma --save-dev npm install karma-jasmine karma-chrome-launcher jasmine-core --save-dev
在package.json
的scripts
配置"test": "karma start"
vue
$ karma init my.conf.js 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 a browser 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". Press Enter to move to the next question. > test/**/*.js > Should any of the files included by the previous patterns be excluded? You can use glob patterns, eg. "**/*.swp". Press Enter 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
配置文件。java
顺便,在根目录建立一个test
的文件夹,在这文件夹中建立一份index.js
,内容为:node
describe('A spec suite', function() { it('contains a passing spec', function() { console.log('Hello Karma') }) })
运行一下:npm run test
咱们会看到程序会自动打开chrome浏览器,而后显示测试结果。webpack
. └── unit ├── index.js ├── karma.conf.js └── specs └── dom.spec.js
由于咱们还要作e2e测试,因此,在test下,用各个文件夹区分,unit下就是单元测试的内容了。git
karma-webpack karma-sourcemap-loader karma-coverage chai sinon sinon-chai karma-sinon-chai karma-mocha-reporter
karma-webpack
:由于咱们的项目代码是用es6或者es7写的,因此webpack编译是必须的karma-sourcemap-loader
:sourcemap明显是必要的karma-coverage
:作代码覆盖率的时候须要用chai
:搭配mocha断言sinon
:搭配mocha作spy、stub、mocksinon-chai
:用chai来作sinon的断言,能够说是扩展karma-sinon-chai
:方便在测试代码中的调用,直接就能用expect、sinon.spy等,不须要每一个文件都importkarma-mocha-reporter
:mocha测试完的报告es6
像karma、mocha、chai、sinon这种测试工具,我不会很详细的介绍,所有都介绍的话内容实在有点多,并且也比较枯燥。想要学习能够看个人参考资料,是我花了大把时间筛选整理出来的。github
const webpackConfig = require('../../webpack.config.test.js') module.exports = function(config) { config.set({ // 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', 'sinon-chai'], // list of files / patterns to load in the browser files: [ './index.js' ], // list of files / patterns to exclude exclude: [ ], // preprocess matching files before serving them to the browser // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor preprocessors: { './index.js': ['webpack', 'sourcemap', 'coverage'] }, // test results reporter to use // possible values: 'dots', 'progress' // available reporters: https://npmjs.org/browse/keyword/karma-reporter reporters: ['mocha', 'coverage'], coverageReporter: { dir: './coverage', reporters: [ { type: 'lcov', subdir: '.' }, { type: 'text-summary' } ] }, . . . // Continuous Integration mode // if true, Karma captures browsers, runs the tests and exits singleRun: true, // Concurrency level // how many browser should be started simultaneous concurrency: Infinity, webpack: webpackConfig, webpackMiddleware: { stats: 'errors-only' } }) }
karma本来在根目录,咱们直接移过来就行了。而后修改的很少,我稍微解释一下:
stats: 'errors-only'
咱们让webpack的编译过程不显示出来,除非编译报错const webpackConfigBase = require('./webpack.config.base.js') const config = Object.assign(webpackConfigBase.config, { // sourcemap 模式 devtool: '#inline-source-map', module: { rules: [ { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ } ] }, }) module.exports = config `` #### 编辑index.js入口文件 这个文件是为了配合`karma-webpack`的,详情见[Alternative Usage](https://github.com/webpack-contrib/karma-webpack#alternative-usage)
// require all test files (files that ends with .spec.js)
// 语法说明:https://doc.webpack-china.org...
const testsContext = require.context('./specs', true, /.spec$/)
testsContext.keys().forEach(testsContext)
// require all src files which in the app/common/js for coverage.
// you can also change this to match only the subset of files that
// you want coverage for.
const srcContext = require.context('../../app/common/js/', true, /.js$/)
srcContext.keys().forEach(srcContext)
测试代码放在`./specs`文件夹下,被测试原文件在`../../app/common/js/`下。这里我只测试一些公共的js文件,若是你须要测试其它,可自行修改。好比一些基于vue的UI组件库,你想要测试全部组件代码,还须要作些配置上的修改,这方面能够参考滴滴的cube-ui项目,挺完整的,覆盖率也很高。 #### 正式写测试代码 编辑`dom.spec.js`文件:
/**
*/
import * as dom from 'common/js/utils/dom.js'
// const expect = require('chai').expect 装过sinon-chai就不须要这句了;sinon同理
describe('utils/dom', () => {
// 测试hasClass
it('hasClass', () => {
const ele = document.createElement('div') ele.className = 'base kitty' // 是否含有base expect(dom.hasClass(ele, 'base')).to.be.equal(true) // 是否含有kitty expect(dom.hasClass(ele, 'kitty')).to.be.equal(true) // 是否含有tom expect(dom.hasClass(ele, 'tom')).to.be.equal(false) // 无参数 expect(dom.hasClass()).to.be.equal(false)
})
// 测试addClass
it('addClass', () => {
const ele = document.createElement('div') ele.className = 'base' // 增长类名kitty dom.addClass(ele, 'kitty') expect(ele.className).to.be.equal('base kitty') // 再增长类名kitty,但愿并不会有重复类名 dom.addClass(ele, 'kitty') expect(ele.className).to.be.equal('base kitty')
})
// 测试removeClass
it('removeClass', () => {
const ele = document.createElement('div') ele.className = 'base kitty' // 删除类名kitty dom.removeClass(ele, 'kitty') expect(ele.className).to.be.equal('base') // 删除不存在的类名 dom.removeClass(ele, 'tom') expect(ele.className).to.be.equal('base')
})
// 测试noce
it('once', () => {
const ele = document.createElement('div') const callback = sinon.spy() dom.once(ele, 'click', callback) // 点击一次 ele.click() expect(callback).to.have.been.calledOnce // 再点击一次,预期应该是不调用callback的,因此依然为calledOnce ele.click() expect(callback).to.have.been.calledOnce
})
})
代码注释已经很清楚啦~ #### 运行测试 先修改下`package.json`配置:`"test:unit": "karma start test/unit/karma.conf.js"` 运行:
➜ construct git:(master) npm run test:unit
> vue-construct@1.0.0 test:unit /Users/Terry/WFE/vue-study/construct
> karma start test/unit/karma.conf.js
START: ℹ 「wdm」: ℹ 「wdm」: Compiled successfully. ℹ 「wdm」: Compiling... ℹ 「wdm」: ℹ 「wdm」: Compiled successfully. 23 04 2018 01:25:39.438:INFO [karma]: Karma v2.0.0 server started at http://0.0.0.0:9876/ 23 04 2018 01:25:39.440:INFO [launcher]: Launching browser Chrome with unlimited concurrency 23 04 2018 01:25:39.448:INFO [launcher]: Starting browser Chrome 23 04 2018 01:25:41.778:INFO [Chrome 66.0.3359 (Mac OS X 10.13.2)]: Connected on socket A9ZeKTNtnUU9MAceAAAA with id 51610088 utils/dom ✔ hasClass ✔ addClass ✔ removeClass ✔ once Finished in 0.008 secs / 0.004 secs @ 01:25:41 GMT+0800 (CST) SUMMARY: ✔ 4 tests completed
=============================== Coverage summary =============================== Statements : 87.12% ( 142/163 ), 14 ignored Branches : 61.25% ( 49/80 ), 22 ignored Functions : 86.11% ( 31/36 ), 5 ignored Lines : 90.79% ( 138/152 ) ===============================
karma 测试框架的前世此生
karma thesis
karma 官网
前端自动化测试工具overview
前端自动化测试解决方案探析
mocha官网
代码测试覆盖率分析
聊一聊前端自动化测试
Sinon指南: 使用Mocks, Spies 和 Stubs编写JavaScript测试
sinon-chai github
论文是个颇有意思的东西,看多了你会惊人地发现,不少大厂有深度的文章其实都是对论文的纯翻译~
另外还参考了vue和滴滴的cube-ui的项目测试部分。