本文做者:Gil Tayar
编译:胡子大哈 javascript翻译原文:blog.huziketang.com/blog/posts/…
英文链接:Testing Your Frontend Code: Part II (Unit Testing)html
转载请注明出处,保留原文连接以及做者信息前端
上一篇文章《测试你的前端代码 - part1(介绍)》中,我介绍了关于前端测试的基本知识,从本文开始将具体介绍测试技术。java
上一节有讨论过,单元测试就是以代码单元为单位进行测试,代码单元能够是一个函数,一个模块,或者一个类。不少人认为大多数测试都应该叫单元测试,其实个人观点仍是那句话,无所谓怎么叫,名字叫什么都行。只要你作了足够多的测试,可以保证你部署到线上的生产代码没有问题就能够了。node
单元测试是最容易理解、也最容易实现的测试方式。给单元测试一个输入,让它自动执行,将输出结果和预期结果作对比看其是否正确(输入能够是一个函数参数,输出就是函数的返回值)。react
在写单元测试的时候,尽可能将你的单元测试独立出来,不要几个单元互相引用。养成这样良好的测试习惯。webpack
第一节中提到过,为了这系列博文,我写了一个计算器应用,后面都会拿它进行测试。理论就讲到这里,一块儿来看一下 Calculator 应用吧,源代码在这里。主要有两个组件: keypad
和 display
,它们自身都是 React 单元,也都没有引用其余单元,后面会介绍如何对它们进行测试。git
(若是你已经看了代码可能已经发现了我没有使用 JSX。由于我不想进行转译。如今 Node 和全部流行的浏览器都已经彻底支持 ES6 了,那么做为一个例子来说,让它直接运行会更好一些。虽然它不能运行在 IE 上,不过也不要紧,若是是一个真实的线上项目,我会进行转译的。)github
还有一个问题是按键和展现的逻辑问题,必需要有代码来控制当点击按键的时候发生什么。这里的按键包括数字键(如“1”,“5”)和操做键(如“+”,“=”)。按一般的作法,我把组件设计成了展现型组件(键盘)和容器型组件。容器型组件在个人 App 中是惟一包含 state 的组件,它要考虑当发生按键行为的时候 App 内在逻辑的问题。web
处理逻辑问题的代码是一个单独的模块——calculator。这个模块对于单元测试是很完美的例子。由于它没有对 I/O 和 UI 的依赖。你也应该尽可能使你的应用逻辑上保持独立——模块不依赖于 I/O 和 UI。
对于 Web 应用来说,I/O 是什么?没有文件和数据库的操做?其实不只仅是这样,还有 Ajax 调用,本地存储,DOM 操做等,对我而言,任何和浏览器 API 有关的都是 I/O 操做。
我是怎么把计算逻辑从 React 组件中分离出来的呢?其实很简单,其内在逻辑是计算,我把他封装到一个模块中就能够了。
这个模块的实现也很容易——它接收一个计算器 state(一个对象)和一个字符(数字或者操做符),返回一个新的计算器 state。若是你用过 Redux,它很像 Redux 的 reducer 模式(若是你没用过 Redux 也不要紧)。可是若是一直由上一个 state 获取下一个 state,怎么能回到初始状态呢?这里还有一个模块叫作 initialState
,经过它能够初始化计算器。计算器的 state 并非彻底黑盒的,它包含了一个字段叫作 display
,能够把你想要展现的 state 显示在计算器应用上。
若是你没有耐心看源代码的话,咱们一块儿来看下这里面最重要的部分,应用中算法的细节其实不重要。
module.exports.initialState = { display: '0', initial: true }
module.exports.nextState = (calculatorState, character) => {
if (isDigit(character)) {
return addDigit(calculatorState, character)
} else if (isOperator(character)) {
return addOperator(calculatorState, character)
} else if (isEqualSign(character)) {
return compute(calculatorState)
} else {
return calculatorState
}
}
//....复制代码
再次强调这里的实现细节并不重要,重要的是模块的设计,它暴露出来的函数很是简单——给一个 state,获得下一个 state。
这就是咱们在 test-calculator
中所作的事情。那么接下来怎么进行测试呢?使用测试框架,目前比较流行的框架是 Mocha ,咱们就用它。不过像 Jest,Jasmine,Tape等框架也都行,随意使用你喜欢的测试框架。
全部的测试框架都相似,写测试代码调用被测函数,经过测试框架运行他们,其中运行它们的代码一般叫作“runner”。
Mocha runner 叫作 “mocha
”,若是你看测试脚本的 package.json
,能够看到:
"scripts": {
...
"test": "mocha 'test/**/test-*.js' && eslint test lib",
...
},复制代码
它会运行 test 文件夹中全部以 test-
开头的文件,你能够复制个人 repo,npm install
后,运行 npm test
本身试试。
(顺便提一句,把全部测试都放在测试目录,而且测试目录放在 package 的根目录是一个公认的 npm package 约定,若是你不想让人以为你不专业的话,最好仍是遵照这一约定。)
运行它,会获得以下输出:
这里有 14 个测试经过的提示信息,若是没经过,就会有红色提示出现。
咱们看下面代码:
const {describe, it} = require('mocha')
const {expect} = require('chai')
const calculator = require('../../lib/calculator')
describe('calculator', function () {
const stream = (characters, calculatorState = calculator.initialState) =>
!characters
? calculatorState
: stream(characters.slice(1),
calculator.nextState(calculatorState, characters[0]))
it('should show initial display correctly', () => {
expect(calculator.initialState.display).to.equal('0')
})
it('should replace 0 in initialState', () => {
expect(stream('4').display).to.equal('4')
})
//...复制代码
首先引入 mocha
和断言常量 expect
,这里只引入咱们须要的函数:describe
,it
和 expect
。接下来引入咱们要测试的模块 calculator
。
准备开始测试,用 it
函数来表达:
it('should show initial display correctly', () => {
expect(calculator.initialState.display).to.equal('0')
})复制代码
it
函数接收一个字符串(用来表示测试结果)和一个函数(待测函数)。it
测试不能单独运行,它们必须组成一个测试组。因此如代码中所示,用 describe
函数定义测试组,里面包含了若干个 it
函数。
测试函数中写什么呢?能够写任何想写的东西,在这个例子中咱们测试了初始状态所显示的是否是 0
。若是咱们本身来实现怎么实现呢,好比能够像以下代码:
if (calculator.initialState.display !== '0')
throw 'failed'复制代码
对于这个问题,上面代码也是能够测出来的。可是 expect
包含了不少特性可使测试变得更简单,好比能够测试数组或者对象是否和一个给定的值相等。这就是单元测试的要点,即运行一个函数,或一组函数,检查其 运行结果 是否和 预期结果 一致。
上面的很简单对吧!其实对于单元测试来说,难的并非单元测试自己,而是分离代码的艺术,把代码尽可能分离成单元可测的模块。单元可测的代码通常都是不依赖于其余模块、不依赖于 I/O 的代码。这是比较困难的,大多数人都倾向于把逻辑代码、I/O 代码和 UI 代码写到一块儿。困难是困难,但不是说作不到,有不少技巧可使用,好比你的代码中有一些验证字段,那么你就能够把验证代码组织到一块儿造成函数,再对这个验证函数进行测试。
注意一个重要的事情——单元测试是在 NodeJS 下运行的!而计算器应用是运行在浏览器端的,上面的生产代码都是在 NodeJS 下进行测试的,这也能够吗?
固然能够。由于咱们的代码是同构的,它能够运行在浏览器端和 NodeJS 上。若是你的代码没有使用任何 I/O,就是说没有对浏览器作任何的特化处理,那么它就没有理由不能运行在 NodeJS 上。另外,若是你使用了 require
,它既能够被本地的 NodeJS 识别,也能够被像 Webpack 同样的打包器识别。你看代码中的 package.json
,就能够看到咱们就是使用了 Webpack,用 require
进行代码打包:
"scripts": {
"build": "webpack && cp public/* dist",
...
}复制代码
代码中使用 require
来引入 React 或者其余模块,这不管是在 NodeJS 中仍是浏览器中都是通用的。
咱们还可使用另外一个测试框架,Karma 。使用它能够在浏览器中运行 Mocha 代码,可是这里表达一下个人浅见:单元测试能在 Node 下运行就在 Node 下运行,由于很容易执行和 debug(固然如今在浏览器中执行也很方便)。而且若是代码不须要转译的话,执行的也很是快。
可是咱们的代码没有在浏览器中测试确实是个问题,由于咱们并不真正地知道代码在浏览器中运行会是什么样子。浏览器中的 JS 执行环境和 NodeJS 环境可能会有微妙的差异。
本文中主要介绍了什么:
require
引入模块、使用 Webpack 来打包模块以使其符合浏览器运行环境。下篇文章咱们介绍端到端测试,把咱们的代码在真实环境(浏览器)中测试。请看下一篇文章《测试你的前端代码 - part3(端到端测试)》。
我最近正在写一本《React.js 小书》,对 React.js 感兴趣的童鞋,欢迎指点。