Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

前端单元测试 #92

Open
PLDaily opened this issue Jan 11, 2018 · 0 comments
Open

前端单元测试 #92

PLDaily opened this issue Jan 11, 2018 · 0 comments

Comments

@PLDaily
Copy link
Owner

PLDaily commented Jan 11, 2018

为保证代码的质量,单元测试必不可少。本文记录自己在学习单元测试过程中的一些总结。

TDD与BDD的区别

TDD属于测试驱动开发,BDD属于行为驱动开发。个人理解其实就是TDD先写测试模块,再写主功能代码,然后能让测试模块通过测试,而BDD是先写主功能模块,z再写测试模块。详见示例

服务端代码测试

所谓服务端代码,指的就是一个node的模块,能在node的环境中运行。以一个项目为例,代码结构如下:

.
├── index.js
├── node_modules
├── package.json
└── test
    └── test.js

前端测试框架主要是MochaJasmine,这里我们选择Mocha,断言库有shouldexpectchai以及node自带的assert。这里我们选择chai,chai中包含了expect、should及assert的书写风格。

npm install mocha chai --save-dev
  • index.js
const getNum = (value) => {
  return value * 2
}

module.exports = getNum
  • test.js
const chai = require('chai')
const expect = chai.expect
const getNum = require('../index')

describe('Test', function() {
  it('should return 20 when the value is 10', function() {
  	expect(getNum(10)).to.equal(20)
  })
})

describe用于给测试用例分组,it代表一个测试用例。

  • package.json
"scripts": {
  "test": "mocha"
}

 需要在全局下安装Mocha

npm install mocha -g

项目目录下执行

npm run test

测试通过
2018-01-11 10 43 02

完成代码测试之后我们再去看看代码测试的覆盖率。测试代码覆盖率我们选择使用istanbul,全局安装

npm install -g istanbul

使用istanbul启动Mocha

istanbul cover _mocha

测试通过,覆盖率100%

2018-01-11 11 15 35

行覆盖率(line coverage):是否每一行都执行了?
函数覆盖率(function coverage):是否每个函数都调用了?
分支覆盖率(branch coverage):是否每个if代码块都执行了?
语句覆盖率(statement coverage):是否每个语句都执行了?

修改index.js再看代码覆盖率

const getNum = (value) => {
  if(value === 0) {
	return 1
  }else {
	return value * 2
  }
}

module.exports = getNum

发现代码覆盖率发生了变化

2018-01-11 11 13 34

修改test.js添加测试用例

describe('Test', function() {
  it('should return 20 when the value is 10', function() {
  	expect(getNum(10)).to.equal(20)
  })
  it('should return 1 when the value is 0', function() {
    expect(getNum(0)).to.equal(0)
  })
})

代码覆盖率又回到了100%

2018-01-11 11 26 17

客户端代码

客户端代码即运行在浏览器中的代码,代码中包含了window、document等对象,需要在浏览器环境下才能起作用。还是以一个项目为例,代码结构如下:

.
├── index.js
├── node_modules
├── package.json
└── test
    └── test.js
    └── test.html

我们依然使用Mocha测试库及chai断言库。

npm install mocha chai --save-dev
  • index.js
window.createDiv = function(value) {
  var oDiv = document.createElement('div')
  oDiv.id = 'myDiv'
  oDiv.innerHTML = value
  document.body.appendChild(oDiv)
}
  • test.js
mocha.ui('bdd')

var expect = chai.expect
describe("Tests", function () {
  before(function () {
    createDiv('test')
  })
  it("content right", function () {
    var el = document.querySelector('#myDiv')
    expect(el).to.not.equal(null)
    expect(el.innerHTML).to.equal("test")
  })
})

mocha.run()
  • test.html
<html>
   <head>
     <title> Tests </title>
     <link rel="stylesheet" href="../node_modules/mocha/mocha.css"/>
   </head>
   <body>
     <div id="mocha"></div>
     <script src="../node_modules/mocha/mocha.js"></script>
     <script src="../node_modules/chai/chai.js"></script>
     <script src="../index.js"></script>
     <script src="./test.js"></script>
   </body>
 </html>

直接用浏览器打开test.html文件便能看到测试结果
2018-01-11 2 15 12

当然我们可以选择PhantomJS模拟浏览器去做测试,这里我们使用mocha-phantomjs对test.html做测试。

全局安装mocha-phantomjs

npm install mocha-phantomjs -g

修改package.json

"scripts": {
  "test": "mocha-phantomjs test/test.html"
}

项目目录下执行

npm run test

mocha-phantomjs在mac下执行会报phantomjs terminated with signal SIGSEGV,暂时没有找到什么解决方案。所以我在ubuntu下执行,结果显示如图

2018-01-11 2 31 45

上述方式虽然能完成代码的单元测试,但是要完成代码覆盖率的计算也没有什么好的方式,所以我选择引入测试管理工具karma

karma使用指南

略去一大堆介绍karma的废话,项目下引入karma

npm install karma --save-dev

初始化配置karma配置文件

npm install karma -g
karma init

使用karma默认配置看看每一项的作用

  • Karma.conf.js
module.exports = function(config) {
  config.set({
    basePath: '', // 设置根目录
    frameworks: ['jasmine'], // 测试框架
    files: [ // 浏览器中加载的文件
    ],
    exclude: [ // 浏览器中加载的文件中排除的文件
    ],
    preprocessors: { // 预处理
    },
    reporters: ['progress'], // 添加额外的插件
    port: 9876, // 开启测试服务时监听的端口
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true, // 监听文件变化,发生变化则重新编译
    browsers: ['Chrome'], // 测试的浏览器
    singleRun: false, // 执行测试用例后是否关闭测试服务
    concurrency: Infinity
  })
}

此时的项目结构如下所示

.
├── index.js
├── node_modules
├── package.json
├── karma.conf.js
└── test
    └── test.js

首先我们将测试框架jasmine改为我们熟悉的mocha及chai,添加files及plugins

npm install karma karma-mocha karma-chai mocha chai karma-chrome-launcher --save-dev
  • karma.conf.js
module.exports = function(config) {
  config.set({
    basePath: '',
    frameworks: ['mocha', 'chai'],
    files: [
        'index.js',
        'test/*.js'
    ],
    exclude: [
    ],
    preprocessors: {
    },
    reporters: ['progress'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['Chrome'],
    singleRun: false,
    concurrency: Infinity,
    plugins: [
      'karma-chrome-launcher',
      'karma-mocha',
      'karma-chai',
    ]
  })
}

全局安装karma-cli

npm install -g karma-cli

修改package.json

"scripts": {
  "test": "karma start karma.conf.js"
}

执行npm run test便可以启用chrome去加载页面。

2018-01-11 5 13 27

karma测试代码覆盖率

测试代码覆盖率,我们还是选择使用PhantomJS模拟浏览器,将singleRun设为true,即执行完测试用例就退出测试服务。

npm install karma-phantomjs-launcher --save-dev

browsers中的Chrome改为PhantomJSplugins中的karma-chrome-launcher改为karma-phantomjs-launcher

执行npm run test ,测试通过且自动退出如图所示

2018-01-11 5 17 32

引入karma代码覆盖率模块karma-coverage,改模块依赖于istanbul

npm install istanbul karma-coverage --save-dev

修改karma.conf.js

module.exports = function(config) {
  config.set({
    basePath: '',
    frameworks: ['mocha', 'chai'],
    files: [
        'index.js',
        'test/*.js'
    ],
    exclude: [
    ],
    preprocessors: {
        'index.js': ['coverage']
    },
    reporters: ['progress', 'coverage'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['PhantomJS'],
    singleRun: true,
    concurrency: Infinity,
    coverageReporter: {
      type : 'text-summary'
    },
    plugins: [
      'karma-phantomjs-launcher',
      'karma-mocha',
      'karma-coverage',
      'karma-chai',
    ]
  })
}

对index.js文件使用coverage进行预处理,加入karma-coverage插件,覆盖率测试输出coverageReporter配置,详见这里这里为了方便截图显示将其设为text-summary

执行npm run test,显示结果如下图所示

2018-01-11 5 30 10

ES6代码覆盖率计算

目前的浏览器并不能兼容所有ES6代码,所以ES6代码都需要经过babel编译后才可在浏览器环境中运行,但编译后的代码webpack会加入许多其他的模块,对编译后的代码做测试覆盖率就没什么意义了。

  • index.js
const createDiv = value => {
  var oDiv = document.createElement('div')
  oDiv.id = 'myDiv'
  oDiv.innerHTML = value
  document.body.appendChild(oDiv)
}

module.exports = createDiv

我们使用ES6中的箭头函数

  • test.js
const createDiv = require('../index')
describe("Tests", function () {
  before(function () {
    createDiv('test')
  })
  it("content right", function () {
    var el = document.querySelector('#myDiv')
    expect(el).to.not.equal(null)
    expect(el.innerHTML).to.equal("test")
  })
})
  • kama.conf.js
module.exports = function(config) {
  config.set({
    basePath: '',
    frameworks: ['mocha', 'chai'],
    files: [
        'test/*.js'
    ],
    exclude: [
    ],
    preprocessors: {
        'index.js': ['coverage'],
        'test/*.js': ['webpack']
    },
    reporters: ['progress', 'coverage'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['PhantomJS'],
    singleRun: true,
    concurrency: Infinity,
    coverageReporter: {
      type : 'text-summary'
    },
    webpack: {
      module: {
        rules: [
          {
            test: /\.js$/,
            exclude: /node_modules/,
            use: {
              loader: 'babel-loader',
              options: {
                "presets": ["es2015"],
                "plugins": [["istanbul"]]
              }
            }
          }
        ]
      }
    },
    plugins: [
      'karma-phantomjs-launcher',
      'karma-mocha',
      'karma-coverage',
      'karma-webpack',
      'karma-chai',
    ]
  })
}

test.js文件通过require引入index.js文件,所以files只需引入test.js文件,再对test.js做webpack预处理。

需要相关的babel,webpack,karma依赖如下:

"devDependencies": {
  "babel-core": "^6.26.0",
  "babel-loader": "^7.1.2",
  "babel-plugin-istanbul": "^4.1.5",
  "babel-preset-es2015": "^6.24.1",
  "chai": "^4.1.2",
  "istanbul": "^0.4.5",
  "karma": "^2.0.0",
  "karma-chai": "^0.1.0",
  "karma-coverage": "^1.1.1",
  "karma-mocha": "^1.3.0",
  "karma-phantomjs-launcher": "^1.0.4",
  "karma-webpack": "^2.0.9",
  "mocha": "^4.1.0",
  "webpack": "^3.10.0"
}

执行npm run dev显示如图所示

2018-01-11 6 03 57

travisCI及coveralls

travisCI的配置这里不做详解,我们将通过代码测试覆盖率上传到coveralls获取一个covarage的icon。

2018-01-11 6 29 18

如果你是服务端代码使用istanbul计算代码覆盖率的

  • .travis.yml
language: node_js
node_js:
- 'stable'
- 8
branches:
  only:
  - master
install:
- npm install
script:
- npm test
after_script: "npm install coveralls && cat ./coverage/lcov.info | coveralls"

如果你是服务端代码使用karma计算代码覆盖率的,则需使用coveralls模块

npm install coveralls karma-coveralls --save-dev
  • Karma.conf.js
// Karma configuration
module.exports = function(config) {
  config.set({
    basePath: '',
    frameworks: ['mocha', 'chai'],
    files: [
      'test/*.js'
    ],
    exclude: [],
    preprocessors: {
      'test/*.js': ['webpack'],
      'index.js': ['coverage']
    },
    reporters: ['progress', 'coverage', 'coveralls'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['PhantomJS'],
    singleRun: true,
    concurrency: Infinity,
    webpack: {
      module: {
        rules: [
          {
            test: /\.js$/,
            exclude: /node_modules/,
            use: {
              loader: 'babel-loader',
              options: {
                "presets": ["es2015"],
                "plugins": [["istanbul"], ["transform-runtime"]]
              }
            }
          }
        ]
      }
    },
    coverageReporter: {
      type : 'lcov',
      dir : 'coverage/'
    },
    plugins: [
      'karma-webpack',
      'karma-phantomjs-launcher',
      'karma-coverage',
      'karma-mocha',
      'karma-chai',
      'karma-coveralls'
    ],
  })
}

plugins添加karma-coveralls,reporters添加coveralls,coverageReporter输出配置改为lcov。

可以参考我自己实现的一个show-toast

参考

tmallfe/tmallfe.github.io#37
https://codeutopia.net/blog/2015/03/01/unit-testing-tdd-and-bdd/
https://github.com/jdavis/tdd-vs-bdd
https://jasmine.github.io/
https://github.com/bbraithwaite/karma-seed
https://mochajs.org/
https://toutiao.io/posts/564973/app_preview
https://coveralls.io/
https://karma-runner.github.io/0.13/index.html
https://github.com/karma-runner/karma-coverage/blob/master/docs/configuration.md
https://shouldjs.github.io/
https://juejin.im/post/59807358518825563e037e3c
http://www.bradoncode.com/blog/2015/02/27/karma-tutorial/
http://docs.casperjs.org/en/latest/quickstart.html
https://github.com/gotwarlost/istanbul
https://www.jianshu.com/p/ffd6d319f05b
http://www.bijishequ.com/detail/436708
http://phantomjs.org/
https://github.com/CurtisHumphrey/es6-library-boilerplate
http://www.jackpu.com/shi-yong-babel-jspm-karma-jasmine-istanbul-shi-xian-es6-ce-shi-fu-gai-lu/
https://github.com/JackPu/JavaScript-Algorithm-Learning
https://github.com/caitp/karma-coveralls
https://github.com/karma-runner/karma-coverage

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant