掌握测试驱动开发的技术

下面是测试驱动开发的基本循环:

  1. 编写测试。理想情况下,它将会失败,通过这种方式确定它正在测试应该测试的功能。

  2. 编写代码让测试通过

  3. 重构代码,让代码DRY并且优美。

  4. 重新这个过程知道程序结果。

补充概念:DRY 全称:Don't Repeat Yourself (摘自wikipedia),是指编程过程中不写重复代码,将能够公共的部分抽象出来,封装成工具类或者用“abstraction”类来抽象公有的东西,降低代码的耦合性,这样不仅提高代码的灵活性、健壮性以及可读性,也方便后期的维护或者修改。

一.在编码之前编写测试

最重要的步骤,也是开发者最怕麻烦的:在编写代码之前编写测试。维护这个原则是极其困难的,但它绝对是可靠性的关键。永远不要让代码出现在测试之前。

当我们传统方式代码之后再编写测试代码,调试编写完的测试代码就不会失败顺利通过。但是如何知道它的通过是因为代码正确。还是因为测试代码本身写的时候就有错误?

一句话白话:编写实际业务代码前先编写测试代码

二.保持测试DRY

通常测试将包含比主体更多的代码。这是一个非常重要的提示:保持测试DRY和保持代码状况良好同样重要。

一句话白话:保持测试代码的灵活性、健壮性以及可读性,便于以后的维护与修改。

三.首先测试错误条件

编写的第一个测试应该是错误条件的测试。原因更多是心理学而不是技术有关的。如果在完成了所有的"真正"测试再来编写这些测试,那么可能你已经厌烦了这个测试主体用例的编写,更可能迫不及待的直接开始搞开始搞下一个任务了。另一方面,如果将最有趣的和最重要的测试保留到最后,那么会让你有值得期待的东西,这样你可以带着热情维持到结束。

除此之外,首先编写错误的测试和边界检查测试将帮助考虑清楚有问题的参数,并可能让你想到一些额外的正面测试。

一句话白话:测试代码先从测试报错结果的业务代码开始。

四.测试要先易后难

为走向快乐测试的道路上来,从最简单的测试开始。这将使测试的主体以最小的增量进行增长。觉得哪种测试方式更好呢:编写小的增量完成单个测试还是编写大的增量完成单个测试。答案非常明显。

一句话白话:从最简单的测试开始也会使测试非常简单。

五.具体

非常容易出现编写的测试并非真正实现了测试目标的情况。例如,在测试错误条件时,不满足抛出来错误就完事了,需要具体的去测试验证是否是目标的报错信息。

一句话白话:不能只局限于跑通过程,要具体验证是否是目标的结果。

六.只测试一件事

每个测试应该只验证一件事情。不过现实一点儿说,一件事指的可能是一组条件。

如下测试代码片段:

it("throws if a widget function is not provided", function(){
  [null, undefined, 1, "SomeString", false].forEach(function testInvalid(val){
    expect(function shouldThrow(){
      var sandbox = new Conference.WidgetSandbox(['tool1', 'tool2'], val);
    }).toThrowError(Conference.WidgetSandbox.messages.fcnMustBeProvided);
  });
});

如果widgetFcn不是一个函数,new WidgetSandbox(toolsArray, widgetFcn)将会抛出异常。该测试将通过forEach中的一些验证一组非函数的参数来测试逻辑,而不是为每种非函数的参数再独立编写一个独立的测试。

一句话白话:测试用例只测一件事,一组条件合并在一起。

七.测试数据如同测试一样重要

避免使用对测试主体有特殊意义的测试数据,这意味着避免使用特殊的数字,例如0和1(除非这些特定的数字与测试点有关),而且要为每块数据使用不同的数字。如果真实生活中的数组是一对多的关系,那么请确保测试数据也是如此,并保证测试一对多的行为。

大多数程序员都对编写代码更有兴趣,而不是编写数据。不要因为缺少兴趣而让自己变得懒惰!

一句话白话:测试数据很重要,认真对待~

八.高效地使用测试框架

拿单元测试框架 jasmine举例:jasmine的describe和it 函数都可以接受字符串为第一个参数,如果为每个嵌套的describe和it组中添加一些单词作为字符串,那么将它们连接起来将组成一个句子,通过这种方式单元测试的输出读起来就像一个函数规范。这样也可以帮助澄清测试的目的。

我们应该会遇到许多这样的例子:测试在单独运行时是通过的,但是作为整个套件的一部分运行时是失败的(也就是整体测试时)。通常,有时因为一些应该在beforeEach中初始化的代码在其他位置被初始化了,造成了代码污染。

另外,如果必要,请确保使用afterEach进行清理,以免影响到别的测试单元。

一句话白话:合理利用好测试框架来高效的编写测试用例。

Last updated