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

前端单元测试 #1

Open
hwen opened this Issue Nov 5, 2017 · 0 comments

Comments

Projects
None yet
1 participant
@hwen
Owner

hwen commented Nov 5, 2017

原文发于我的博客:#1

写在前面:本文主要介绍一下前端单元测试,并提供一个单元测试的例子(模板),主要是例子为主,通过尝试就能慢慢上手前端单元测试。并记录自己的一些单元测试调试技巧。

要不要写单测?

关于这个 cnode 上就有个很有意思的讨论

做个调查,你的 Node 应用有写单测吗?

看完这个应该会有结论?如果没有,就回帖跟别人探讨下~

测试

测试有分为

  • 单元测试(单测)
  • 集成测试
  • 系统测试

主要区别是单测倾向于测试模块内部运行逻辑及功能,集成测试倾向于模块间互相组合跟调用的测试。
系统测试(当然你要说,还有自动化测试)是对整个系统的测试,主要由测试人员而非开发人员负责。

本文只讨论单测的范畴,对集成测试有兴趣的话,可以看下 Vue 的集成测试代码。

前端单测现状

测试本质上就是假定一个输入,然后判断得到预期的输出。而前端与后端相比,夹杂着浏览器 DOM 操作,异步请求,
浏览器兼容性等方面的。要我来说,比后端写单测要麻烦多了。。。

不过现在前端的单测已经发展得比较完善了,已经有一套比较完整的工具链,来完成各种需求。

单测工具链

框架

目前比较流行的测试框架有:

  • jasmine: 自带断言(assert),mock 功能
  • mocha:框架不带断言和mock功能,需要结合其他工具。mocha 是 tj 大神写的(tj 就是那个写了express、koa、n、co、ejs的人。。。)

用得比较多的就上面两个,还有一些用得比较少的,比如 Qunitintern

框架的实现原理其实就是检测内部运行的代码是否有抛出异常。而断言库如果没有得到预期的输入时,就会抛出异常,给框架检测到。

PS.想要学 mocha 和单测写法,最好的资源就是 express 框架的测试代码(狼叔推荐,亲测很不错)

断言库

  • chai:目前比较流行的断言库,支持 TDD(assert),BDD(expect、should)两种风格
  • shouldjs:也是 tj 写的,说实话我比较喜欢这个,但是有坑。。。后面会说到。。。
  • expectjs:基本是 shouldjs 的缩水版
  • assert:node 的核心模块,node 环境可以直接使用

mock 库

  • sinon.js:只用过这个,其他不大清楚。不过这个是目前最多人用的,基本够了。支持 spies, stub, fake XMLHttpRequest, Fake server, Fake time,很强大

测试集成工具

  • karma:Google Angular 团队写的,功能很强大,有很多插件
  • buster.js: 这个支持 node 环境,但目前还在 beta 版(2017/11/4,版本0.7)。官网

半小时上手单测

(并不是学会,就是这么耿直 (:з)∠),手是谁???)

使用的是:karma + mocha + chai + sinon(如果之前没了解过这几个,可以边写边看文档,这样学会快很多)

完整的例子可以在这里找到:GitHub 项目
建议把项目 clone 下来自己跑一遍,然后可以自己加一些特效(啊,不对,是代码才对。。。

测试 DOM

详细代码,及配置见 源码

it('test dom', () => {
  const div = document.getElementById('test')

  const content = div.innerHTML
  content.should.be.equal('here')

  setDivContent()
  const after = div.innerHTML
  after.should.be.equal('hallo world')
})

测试异步请求

it('test async', done => {
  getTopics()
    .then(res => {
      res.success.should.be.equal(true)
      done()
    })
    .catch(err => {
      info(err)
      done(err)
    })

})

调试技巧

调试技巧之一:善用 only

当你的单元测试越写越多时,想测试新写的单测是否正确时,可以用 mocha 的 only
这样做的好处有二:第一屏蔽其他测试可以使测试速度变得更快,第二屏蔽其他测试,可以在你新写的测试错误时
确定这个错误不是被其他测试所影响导致的。

用法

describe.only('something', function() {
  // 只会跑包在里面的测试
})

或者

it.only('do do', () => {
  // 只会跑这一个测试
})

调试技巧之二:善用 debug

要开启 debug 的话,先在 karma.conf.jssingleRun 改成 false
然后,看图(懒得打字了 (:з)∠)

debug

覆盖率

生成覆盖率报告也是相当简单,不过有个要注意的地方就是
现在前端代码很多都是经过 webpackbabel 编译的,生成的代码会多了很多二外的代码

要解决这个问题使用babel-plugin-istanbul来替代karma-coverage就可以了

    preprocessors: {
      'src/**/*.js': ['webpack', 'coverage'],
      'test/*.test.js': ['webpack']
    }

将 karma-coverage 去掉,变成

    preprocessors: {
      'src/**/*.js': ['webpack'],
      'test/*.test.js': ['webpack']
    }

然后在 webpack 配置添加 istanbul 插件

  use: {
    loader: 'babel-loader',
    options: {
      plugins: ['istanbul']
    }
  }

最后可以生成覆盖率报告

cover1
cov2

另外有很多工具可以对生成的覆盖率报告进行进一步的分析,比如最常见的
你会在 Github 上经常见到的图标

cov-icon

这个就是利用报告里的lcovonly分析生成的

coverageReporter: {
  dir: 'test/coverage/',
  reporters: [
    { type: 'html', subdir: 'report-html' },
    { type: 'lcovonly', subdir: '.', file: 'report-lcovonly.txt' },  // 这里,你可以重命名 file
    { type: 'text-summary', subdir: '.', file: 'text-summary.txt' }
  ]
}

一些坑

  • 测试时如果有涉及浏览器事件(addEventListener),记得测试完移除掉,不然会对其他的测试造成影响(afterEach -> removeEventListener)
  • mocha 里使用箭头函数需要注意,因为箭头函数的 this 指向是静态的,所以 this 并不指向 mocha(没有 mocha 上下文)
  • 上面有说到,如果测试的是经过编译的代码,需要进行一些配置,目前的办法是用babel-plugin-istanbul代替karma-cover来检测覆盖率

补充

前面有说到,为什么不用 should.js??
因为如果你用 ES6 的语法写单测(webpack 编译),用 import should-sinon 会报错。。
(是不是因为 tj 大神写完东西不喜欢维护的习惯导致should.js支持性不好???)

@hwen hwen added the 工程化 label Nov 12, 2017

@hwen hwen changed the title from 前端单元测试 all in one to 前端单元测试 Feb 9, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment