多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
开始测试这份代码前,我需要一个测试框架。JavaScript世界里这样的框架有很多,这里我选用的是使用度和声誉都还不错的Mocha。我不打算全面讲解框架的使用,而只会用它写一些测试作为例子。看完之后,你应该能轻松地学会用别的框架来编写类似的测试。 以下是为缺额计算过程编写的一个简单的测试: ``` describe('province', function() {  it('shortfall', function() {   const asia = new Province(sampleProvinceData());   assert.equal(asia.shortfall, 5);  }); }); ``` Mocha框架组织测试代码的方式是将其分组,每一组下包含一套相关的测试。测试需要写在一个`it`块中。对于这个简单的例子,测试包含了两个步骤。第一步设置好一些测试夹具(fixture),也就是测试所需要的数据和对象等(就本例而言是一个加载好了的行省对象);第二步则是验证测试夹具是否具备某些特征(就本例而言则是验证算出的缺额应该是期望的值)。 > 不同开发者在`describe`和`it`块里撰写的描述信息各有不同。有的人会写一个描述性的句子解释测试的内容,也有人什么都不写,认为所谓描述性的句子跟注释一样,不外乎是重复代码已经表达的东西。我个人不喜欢多写,只要测试失败时足以识别出对应的测试就够了。 如果我在NodeJS的控制台下运行这个测试,那么其输出看起来是这样: ``` '''''''''''''' 1 passing (61ms) ``` 它的反馈极其简洁,只包含了已运行的测试数量以及测试通过的数量。 当我为类似的既有代码编写测试时,发现一切正常工作固然是好,但我天然持怀疑精神。特别是有很多测试在运行时,我总会担心测试没有按我期望的方式检查结果,从而没法在实际出错的时候抓到bug。因此编写测试时,我想看到每个测试都至少失败一遍。我最爱的方式莫过于在代码中暂时引入一个错误,像这样: > ![](https://box.kancloud.cn/9cf522e33e311401bf0d755d003df8ea_19x20.jpeg) 总是确保测试不该通过时真的会失败。 ##### class Province... ``` get shortfall() {  return this._demand - this.totalProduction * 2; } ``` 现在控制台的输出就有所改变了: ``` !  0 passing (72ms)  1 failing 1) province shortfall:   AssertionError: expected -20 to equal 5   at Context.<anonymous> (src/tester.js:10:12) ``` 框架会报告哪个测试失败了,并给出失败的根本原因——这里是因为实际算出的值与期望的值不相符。于是我总算见到有什么东西失败了,并且还能马上看到是哪个测试失败,获得一些出错的线索(这个例子中,我还能确认这就是我引入的那个错误)。 一个真实的系统可能拥有数千个测试。好的测试框架应该能帮我简单快速地运行这些测试,一旦出错,我能马上看到。尽管这种反馈非常简单,但对自测试代码来说却尤为重要。工作时我会非常频繁地运行测试,要么是检验新代码的进展,要么是检查重构过程是否出错。 > ![](https://box.kancloud.cn/9cf522e33e311401bf0d755d003df8ea_19x20.jpeg) 频繁地运行测试。对于你正在处理的代码,与其对应的测试至少每隔几分钟就要运行一次,每天至少运行一次所有的测试。 Mocha框架允许使用不同的库(它称之为断言库)来验证测试的正确性。JavaScript世界的断言库,连在一起都可以绕地球一周了,当你读到这里时,可能有些仍然还没过时。我现在使用的库是Chai,它可以支持我编写不同类型的断言,比如“assert”风格的: ``` describe('province', function() {  it('shortfall', function() {   const asia = new Province(sampleProvinceData());   assert.equal(asia.shortfall, 5);  }); }); ``` 或者是“expect”风格的: ``` describe('province', function() {  it('shortfall', function() {   const asia = new Province(sampleProvinceData());   expect(asia.shortfall).equal(5);  }); }); ``` 一般来讲我更倾向于使用assert风格的断言,但使用JavaScript时我倒是更常使用expect的风格。 环境不同,运行测试的方式也不同。使用Java编程时,我使用IDE的图形化测试运行界面。它有一个进度条,所有测试都通过时就会显示绿色;只要有任何测试失败,它就会变成红色。我的同事们经常使用“绿色条”和“红色条”来指代测试的状态。我可能会讲“看到红条时永远不许进行重构”,意思是:测试集合中还有失败的测试时就不应该先去重构。有时我也会讲“回退到绿条”,表示你应该撤销最近一次更改,将测试恢复到上一次全部通过的状态(通常是切回到版本控制的最近一次提交点)。 图形化测试界面的确很棒,但并不是必需的。我通常会在Emacs中配置一个运行测试的快捷键,然后在编译窗口中观察纯文本的反馈。要点在于,我必须能快速地知道测试是否全部都通过了。