资讯专栏INFORMATION COLUMN

前端单元测试

liuyix / 3181人阅读

摘要:为保证代码的质量,单元测试必不可少。本文记录自己在学习单元测试过程中的一些总结。以一个项目为例,代码结构如下前端测试框架主要是与,这里我们选择,断言库有以及自带的。

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

TDD与BDD的区别

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

服务端代码测试

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

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

前端测试框架主要是Mocha与Jasmine,这里我们选择Mocha,断言库有should、expect、chai以及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

测试通过

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

npm install -g istanbul

使用istanbul启动Mocha

istanbul cover _mocha

测试通过,覆盖率100%

行覆盖率(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

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

修改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%

客户端代码

客户端代码即运行在浏览器中的代码,代码中包含了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


   
      Tests 
     
   
   
     

直接用浏览器打开test.html文件便能看到测试结果

当然我们可以选择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下执行,结果显示如图

上述方式虽然能完成代码的单元测试,但是要完成代码覆盖率的计算也没有什么好的方式,所以我选择引入测试管理工具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去加载页面。

karma测试代码覆盖率

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

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

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

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

引入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,显示结果如下图所示

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显示如图所示

travisCI及coveralls

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

如果你是服务端代码使用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库

参考

https://github.com/tmallfe/tm...
https://codeutopia.net/blog/2...
https://github.com/jdavis/tdd...
https://jasmine.github.io/
https://github.com/bbraithwai...
https://mochajs.org/
https://toutiao.io/posts/5649...
https://coveralls.io/
https://karma-runner.github.i...
https://github.com/karma-runn...
https://shouldjs.github.io/
https://juejin.im/post/598073...
http://www.bradoncode.com/blo...
http://docs.casperjs.org/en/l...
https://github.com/gotwarlost...
https://www.jianshu.com/p/ffd...
http://www.bijishequ.com/deta...
http://phantomjs.org/
https://github.com/CurtisHump...
http://www.jackpu.com/shi-yon...
https://github.com/JackPu/Jav...
https://github.com/caitp/karm...
https://github.com/karma-runn...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/93051.html

相关文章

  • 测试你的前端代码 - part1(介绍篇)

    摘要:测试光谱光谱的一端单元测试顾名思义,代码以单元为单位进行测试。这个系列文章整体如下测试你的前端代码单元测试测试你的前端代码端到端测试测试你的前端代码集成测试。 showImg(https://segmentfault.com/img/remote/1460000008812278?w=998&h=354); 本文作者:Gil Tayar 编译:胡子大哈 翻译原文:http://hu...

    helloworldcoding 评论0 收藏0
  • 测试你的前端代码 - part1(介绍篇)

    摘要:测试光谱光谱的一端单元测试顾名思义,代码以单元为单位进行测试。这个系列文章整体如下测试你的前端代码单元测试测试你的前端代码端到端测试测试你的前端代码集成测试。 showImg(https://segmentfault.com/img/remote/1460000008812278?w=998&h=354); 本文作者:Gil Tayar 编译:胡子大哈 翻译原文:http://hu...

    jimhs 评论0 收藏0
  • 前端进阶之路: 前端架构设计(3) - 测试核心

    摘要:而测试驱动开发技术并不只是单纯的测试工作。需求向来就是软件开发过程中感觉最不好明确描述易变的东西。这里说的需求不只是指用户的需求,还包括对代码 可能很多人和我一样, 首次听到前端架构这个词, 第一反应是: 前端还有架构这一说呢? 在后端开发领域, 系统规划和可扩展性非常关键, 因此架构师备受重视, 早在开发工作启动之前, 他们就被邀请加入到项目中, 而且他们会跟客户讨论即将建成的平台的...

    Karuru 评论0 收藏0
  • 前端进阶之路: 前端架构设计(3) - 测试核心

    摘要:而测试驱动开发技术并不只是单纯的测试工作。需求向来就是软件开发过程中感觉最不好明确描述易变的东西。这里说的需求不只是指用户的需求,还包括对代码 可能很多人和我一样, 首次听到前端架构这个词, 第一反应是: 前端还有架构这一说呢? 在后端开发领域, 系统规划和可扩展性非常关键, 因此架构师备受重视, 早在开发工作启动之前, 他们就被邀请加入到项目中, 而且他们会跟客户讨论即将建成的平台的...

    宋华 评论0 收藏0
  • 测试你的前端代码 - part2(单元测试

    摘要:单元测试上一节有讨论过,单元测试就是以代码单元为单位进行测试,代码单元可以是一个函数,一个模块,或者一个类。单元测试是最容易理解也最容易实现的测试方式。在写单元测试的时候,尽量将你的单元测试独立出来,不要几个单元互相引用。 showImg(https://segmentfault.com/img/remote/1460000008823416?w=997&h=350); 本文作者:G...

    daydream 评论0 收藏0

发表评论

0条评论

liuyix

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<