> For the complete documentation index, see [llms.txt](https://tdd.shujuwajue.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://tdd.shujuwajue.com/shi-jian/ru-he-zhang-wo-tdd.md).

# 掌握测试驱动开发的技术

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

1. 编写测试。理想情况下，它将会失败，通过这种方式确定它正在测试应该测试的功能。
2. 编写代码让测试通过
3. 重构代码，让代码**DRY**并且优美。
4. 重新这个过程知道程序结果。

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

## 一．在编码之前编写测试

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

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

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

## 二．保持测试DRY

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

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

## 三．首先测试错误条件

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

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

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

## 四．测试要先易后难

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

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

## 五．具体

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

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

## 六．只测试一件事

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

如下测试代码片段：

```javascript
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中的一些验证一组非函数的参数来测试逻辑，而不是为每种非函数的参数再独立编写一个独立的测试。

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

## 七．测试数据如同测试一样重要

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

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

> 一句话白话：测试数据很重要，认真对待～

## 八．高效地使用测试框架

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

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

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

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