测试驱动开发
---

测试驱动开发是一个很"古老"的程序开发方法，然而由于国内的开发流程的问题——即开发人员负责功能的测试，导致这么好的一项技术没有在国内推广。

###红-绿-重构

测试驱动开发的主要过程是: 红 —> 绿 -> 重构

![TDD](chapters/chapter3/tdd.jpg)

1. 先写一个失败的单元测试。即我们并没有实现这个方法，但是已经有了这个方法的测试。
2. 让测试通过。实现简单的代码来保证测试通过，就算我们用一些作弊的方法也是可以的。我们写的是功能代码，那么我们应该提交代码，因为我们已经实现了这个功能。
3. 重构，并改进功能代码，让它变得更加合理。

TDD有助于我们将问题分解成更小的部分，再一点点的添加我们所需要的业务代码。随着这个过程的不断进行，我们会发现我们已经接近完成我们的功能代码了。并且到了最后，我们会发现我们的代码都会被测试到。


虽然说起来看上去很简单，但是真正实现起来并不是那么容易。于我而言我只会在我自己造的一些轮子中使用TDD。因为这个花费大量的时间，通常来说测试代码和功能代码的比例可能是1:1，或者是2：1等等。在自己创建的一些个人应用，如博客中，我不需要与其他人Share我的Content。由于我使用的是第三方框架，框架本身的测试已经足够多，并且没有复杂的逻辑，我就没有对我的博客写测试。而在我写的一些框架里，我就会尽量保证足够高的测试覆盖率，并且在适当的时候会去TDD。

通常来说对于单元测试我会采用TDD的方式来进行，但是功能测试仍会选择在最后添加进去。主要的缘由是：在写UI的过程中，元素会发生变化。这一点和我们在写Unit的时候，有很大的区别。div + class会使得我们思考问题的方式发生变化，我们需要去点击某个元素，并观察某个元素发生的变化。而多数时候，我们很难把握好一个页面最好的样子。

不得不说明的一点是，TDD需要你对测试比较了解后，你才容易使用它。从个人的感受来说，TDD是在一开始是一件很难的事。

###测试先行

对于写测试的人来说，测试先行有点难以理解，而对于不写测试的人来说，就更难以理解。这里假定你已经开始写测试了，因为对于不写测试的人来说，写测试就是一件难以理解的事。既然我们都要写测试，那么为什么我们就不能先写测试呢？或者说为什么后写测试存在一些问题？

依据J.Timothy King所总结的《测试先行的12个好处》：

1. 测试可证明你的代码是可以解决问题的
2. 一面写单元测试，一面写实现代码，这样感觉更有兴趣
3. 单元测试也可以用于演示代码
4. 会让你在写代码之前做好计划
5. 它降低了Bug修复的成本
6. 可以得到一个底层模块的回归测试工具
7. 可以在不改变现有功能的基础上继续改进你的设计
8. 可以用于展示开发的进度
9. 它真实的为程序员消除了工作上的很多障碍
10. 单元测试也可以让你更好的设计
11. 单元测试比代码审查的效果还要好
12. 它比直接写代码的效率更高

但是在我个人的感觉里，多比较喜欢的是： **写出可以测试的函数**。这是一个一直困扰着我的难题，特别是当我的代码里存在很多条件的时候，在后期我编写的时候，难道越来越大。当我只有一个简单的IF-ELSE的时候，我的代码测试起来也很简单:

```javascript
if (hour < 18) {
    greeting = "Good day";
} else {
    greeting = "Good evening";
}
```

而当我有复杂的业务逻辑时，后写测试就会变成一场恶梦：

```javascript
if (EchoesWorks.isObject(words)) {
	var nextTime = that.parser.parseTime(that.data.times)[currentSlide + 1];
	if (that.time < nextTime && words.length > 1) {
		var length = words.length;
		var currentTime = that.parser.parseTime(that.data.times)[currentSlide];
		var time = nextTime - currentTime;
		var average = time / length * 1000;
		var i = 0;
		document.querySelector('words').innerHTML = words[i].word;

		timerWord = setInterval(function () {
			i++;
			if (i - 1 === length) {
				clearInterval(timerWord);
			} else {
				document.querySelector('words').innerHTML = words[i].word;
			}
		}, average);
	}
	return timerWord;
} else {
	document.querySelector('words').innerHTML = words;
}
```	

我们需要重新理清业务的逻辑，再依据这些逻辑来编写测试代码。而当我们已经忘记具体的业务逻辑时，我们已然无法写出测试。

**思考**

通常在我的理解下，TDD是可有可无的。既然我知道了我要实现的大部分功能，而且我也知道如何实现。与此同时，对Code Smell也保持着警惕、要保证功能被测试覆盖。那么，总的来说TDD带来的价值并不大。

然而，在当前这种情况下，我知道我想要的功能，但是我并不理解其深层次的功能。我需要花费大量的时候来理解，它为什么是这样的，需要先有一些脚本来知道它是怎么工作的。TDD变显得很有价值，换句话来说，在现有的情况下，TDD对于我们不了解的一些事情，可以驱动出更多的开发。毕竟在我们完成测试脚本之后，我们也会发现这些测试脚本成为了代码的一部分。

在这种理想的情况下，我们为什么不TDD呢?

参考资料

 J.Timothy King 《Twelve Benefits of Writing Unit Tests First》
