上篇文章《测试你的前端代码 - part1(介绍)》介绍了前端测试的基础知识。从本文开始,我将详细介绍测试技术。1.单元测试上一节讲到,单元测试是以代码单元为单位进行测试,可以是一个函数,一个模块,或者一个类。很多人认为大部分测试都应该叫做单元测试。其实我的观点还是那句话。只要你做了足够的测试,确保你部署到线上的生产代码没有问题。单元测试是理解和实施测试的最简单方法。给单元测试一个输入,让它自动执行,将输出结果与预期结果进行比较,看是否正确(输入可以是函数参数,输出是函数的返回值)。在编写单元测试时,尽量将你的单元测试分开,不要在几个单元之间相互引用。养成这样好的测试习惯。2.测试Calculator应用***部分已经提到,为了这一系列的博文,我写了一个计算器应用,后面会进行测试。理论就这些了,让我们看一下计算器应用程序,源代码在这里。有两个主要组件:keypad和display,它们都是React单元,不引用其他单元。我们稍后会介绍如何测试它们。(如果你看过代码,你可能会注意到我没有使用JSX。因为我不想转译。现在Node和所有流行的浏览器都完全支持ES6,最好有直接作为例子运行比较好,虽然不能在IE上运行,但是没关系,如果是真正的在线项目,我会翻译。)按钮和逻辑也有问题显示。必须有代码来控制什么时候点击按钮会发生什么。这里的按键包括数字键(如“1”、“5”)和操作键(如“+”、“=”)。按照惯例,我把组件设计成展示组件(键盘)和容器组件。容器组件是我App中唯一包含state的组件,它需要考虑按下按钮时App的内部逻辑。3.“计算器”模块处理逻辑问题的代码是一个单独的模块——计算器。这个模块是单元测试的一个很好的例子。因为它不依赖于I/O和UI。您还应该尝试让您的应用程序在逻辑上保持独立——模块不依赖于I/O和UI。对于Web应用程序,I/O是什么?没有文件和数据库操作?不仅如此,还有Ajax调用、本地存储、DOM操作等。对我来说,任何与浏览器API相关的东西都是I/O操作。如何将计算逻辑与React组件分离?其实很简单。里面的逻辑就是计算,我可以把它封装成一个模块。这个模块的实现也很简单——它接受一个计算器状态(一个对象)和一个字符(数字或运算符),并返回一个新的计算器状态。如果你用过Redux,它很像Redux的reducer模式(没用过也没关系)。但是如果一直从前一个状态得到下一个状态,又怎么能回到初始状态呢?还有一个叫做initialState的模块,通过它可以对计算器进行初始化。计算器的状态并不是完全的黑盒子,它包含一个叫做display的字段,可以在计算器应用程序上显示你想要显示的状态。如果你没有耐心看源码,那我们就来看看这里最重要的部分吧。应用程序中的算法细节并不重要。module.exports.initialState={display:'0',initial:true}module.exports.nextState=(calculatorState,character)=>{if(isDigit(character)){returnaddDigit(calculatorState,character)}elseif(isOperator(character)){returnaddOperator(calculatorState,character)}elseif(isEqualSign(character)){returncompute(calculatorState)}else{returncalculatorState}}//...同样,这里的实现细节并不重要,重要的是模块设计,它暴露的功能很简单——给一个状态,得到下一个状态。这就是我们在测试计算器中所做的。那么我们接下来如何测试呢?使用一个测试框架,比较流行的框架是Mocha,我们就用它。但Jest、Jasmine、Tape等框架也可以,请随意使用您喜欢的测试框架。4.使用Mocha进行单元测试所有的测试框架都是相似的。编写测试代码以调用被测函数并通过测试框架运行它们。运行它们的代码通常称为“runner”。Mocha运行程序称为“mocha”,如果您查看测试脚本的package.json,您会看到:“scripts”:{..."test":"mocha'test/**/test-*.js'&&eslinttestlib",...},它会运行test文件夹下所有以test-开头的文件,你可以复制我的repo,npminstall后,运行npmtest自己试试。(顺便说一句,把所有的测试都放在test目录下,test目录放在包的根目录下,这是一个公认的npm包约定。如果你不想让人觉得你不专业,最好是按照这个约定。)运行它,你会得到如下输出:有14个测试通过提示,如果没有通过,会有红色提示。让我们看看下面的代码:const{describe,it}=require('mocha')const{expect}=require('chai')constcalculator=require('../../lib/calculator')describe('calculator',function(){conststream=(characters,calculatorcalculatorState=calculator.initialState)=>!characters?calculatorState:stream(characters.slice(1),calculator.nextState(calculatorState,characters[0]))it('shouldshowinitialdisplaycorrectly',()=>{expect(calculator.initialState.display).to.equal('0')})it('应该替换0initialState',()=>{expect(stream('4').display).to.equal('4')})//...先介绍mocha和断言常量expect,这里只介绍我们需要的函数:describe、it和expect。接下来介绍我们要测试的模块计算器。准备开始测试,使用它的函数来表达:it('shouldshowinitialdisplaycorrectly',()=>{expect(calculator.initialState.display).to.equal('0')})it函数接收一个字符串(用来表示测试结果)和一个函数(要测试的函数)。它测试s不能单独运行,必须组成测试组。所以如代码所示,使用describe函数定义测试组,其中包含几个it函数。测试函数要写什么?你可以写任何你想写的东西,在这个例子中我们测试初始状态是否显示为0。我们如何自己实施?比如可以用下面的代码:if(calculator.initialState.display!=='0')throw'failed'对于这个问题,也可以测试上面的代码。但是expect包含许多使测试更容易的特性,例如测试数组或对象是否等于给定值。这就是单元测试的要点,就是运行一个函数,或者一组函数,检查它的运行结果是否和预期的结果一致。5.编写可单元测试的代码上面很简单,对吧!其实对于单元测试来说,难点不在于单元测试本身,而在于分离代码的艺术,尽可能将代码分离成可单元测试的模块。可单元测试的代码一般是不依赖于其他模块、不依赖于I/O的代码。这个比较难,大部分人倾向于把逻辑代码、I/O代码和UI代码写在一起。难是难,但不代表做不到。有许多技术可以使用。比如你的代码中有一些验证字段,那么你可以把这些验证码组织起来组成一个函数,然后测试这个验证函数。6.测试代码在NodeJS下运行!?请注意一件重要的事情——单元测试是在NodeJS下运行的!计算器应用是运行在浏览器端的,上面的生产代码是在NodeJS下测试的,可以吗?当然。因为我们的代码是同构的,所以它既可以在浏览器上运行,也可以在NodeJS上运行。如果你的代码不使用任何I/O,即不做任何浏览器特化,那么它没有理由不能在NodeJS上运行。此外,如果您使用require,它会被原生NodeJS和打包器(如Webpack)识别。如果查看代码中的package.json,可以看到我们使用了Webpack,并使用require来打包代码:"scripts":{"build":"webpack&&cppublic/*dist",...}代码使用require导入React或其他模块,这在NodeJS和浏览器中都很常见。七、在浏览器中运行单元测试我们还可以使用另一个测试框架,Karma。可以用来在浏览器中运行mocha代码,不过这里是个人拙见:单元测试可以在Node下运行,因为执行和调试方便(当然现在在浏览器中执行也很方便)。而且如果代码不需要翻译,执行起来也非常快。但是我们的代码没有在浏览器中测试确实是一个问题,因为我们真的不知道代码在浏览器中运行时会是什么样子。浏览器中的JS执行环境与NodeJS环境可能存在细微差别。8.总结本文主要介绍的内容:介绍如何使用Mocha(和Chai)创建单元测试;介绍单元测试是以代码为单位进行测试,独立于其他模块。描述应如何独立于其他模块设计模块。如果一定要有依赖,可以mock另外一个模块,对这个模块进行单元测试或者集成测试。介绍了我们测试的代码单元应该是同构的,这样才能在NodeJS环境下进行测试。介绍如何编写同构代码——无I/O操作,使用require导入模块,使用Webpack打包模块使其符合浏览器的运行环境。在下一篇文章中,我们将介绍端到端测试,并在真实环境(浏览器)中测试我们的代码。请看下篇文章《测试你的前端代码 - part3(端到端测试)》。点击《测试你的前端代码 - part2(单元测试)》阅读原文。【本文为专栏作者“虎子打哈”原创文章,转载请联系作者获得授权】点此阅读更多该作者好文
