测试驱动开发原则 翻译自<<Expert Python Programming>>
测试驱动开发是指首先编写包含所有测试软件特点的测试集,然后再去开发软件。也就是说,在编写软件之前先把这个软件的测试文档写清楚。举个例子,如果有个程序员想编写一个可以计算一组数字平均值的函数,那我们先要写出这个函数是怎么用的。我们可以这样写:
assert average(1, 2, 3) == 2
assert average(1, -3) == -1
这些测试例子也可以由别的人来负责编写。现在,这个函数如果通过了这两个测试那么就可以拿出去用了:
>>> def average(*numbers):
... return sum(numbers) / len(numbers)
...
>>> assert average(1, 2, 3) == 2
>>> assert average(1, -3) == -1
但是我们新增的测试实例却发现了一个错误:
>>> assert average(0, 1) == 0.5
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError
我们可以根据这个错误来对代码进行修改:
>>> def average(*numbers):
... # makes sure all numbers can be used as floats
... numbers = [float(number) for number in numbers]
... return sum(numbers) / float(len(numbers))
...
>>> assert average(0, 1) == 0.5
并且我们还可增加更多的测试实例:
>>> try:
... average()
... except TypeError:
... # we want an empty sequence to throw a type error
... pass
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "<stdin>", line 3, in average
ZeroDivisionError: integer division or modulo by zero
>>>
>>> def average(*numbers):
... if numbers == ():
... raise TypeError((‘You need to provide at ‘
... ‘least one number‘))
... numbers = [float(number) for number in numbers]
... return sum(numbers) / len(numbers)
...
>>> try:
... average()
... except TypeError:
... pass
...
这里我们可以编写一个测试函数,函数里面包含了我们要使用的所有测试,每次修改我们的代码后就可以运行着个函数:
>>> def test_average():
... assert average(1, 2, 3) == 2
... assert average(1, -3) == -1
... assert average(0, 1) == 0.5
... try:
... average()
... except TypeError:
... pass
...
>>> test_average()
每次我们修改代码之后,我们也会相应的在test_average里面增加新的测试集。同时我们也要保证之前的测试集可以通过。最好的办法是我们把对应模块的所有测试集都放在一个文件夹下面,每个模块对应一个测试文件夹。驱动测试开发可以为我们带来这些好处:
- 防止代码回归
- 提高代码质量
- 提供最简便的文档
- 快速的创造健壮代码
防止代码回归
身为程序员,我们都碰到过软件回归这类问题。软件回归是指我们在对代码的修改过程中导致了其它的错误。这类问题发生通常是因为我们对当前代码作出的改动所带来的影响不够清楚。对这里代码的小小改动有可能影响了其它功能的使用,而且有时候可能导致非常严重的后果,比方说直接毁坏了当前的数据。
为了避免这类事情的发生,我们应该在每一次修改代码之后都去测试把这个软件的所有测试集合都来测试一遍。
如果是同时有几个人在对代码进行修改的话可能使得这类问题更加的严重,因为每个人都不会完全明白软件的每个部分是干什么的。虽然版本控制系统防止了这类冲突的发生,但并不意味着他能防止这次代码修改所造成的潜在问题。
测试驱动开发能够帮助减少软件回归这类问题。在每一次代码修改过后我们都会自动的对软件进行所有的测试。但前提是我们有一个正确的开发软件测试集。当我们使用测试驱动开发的时候,我们的测试集会随着我们代码的改变而增加。
提高代码质量
当我们编写了一个新的模块,类或者函数的时候,程序员大多都只关注自己的代码如何才能更加高效的运转。但当他们在考虑这些的时候,他们很可能会忽略了一点:他们现在编写的这个函数是何时何地怎样去使用的?是不是所有的参数都是方便使用而且符合逻辑的?它命名的API名称是否符合规范?
解决这一问题的唯一方法就是撰写使用规范。
提供最好的开发文档
对开发者而言测试文档是最能够说明软件是如何工作的东西。它们告诉了开发者这个软件如何使用。通过阅读这些测试文档开发者可以快速而且深入的了解这些代码的功效。有时一个例子胜过千言万语。更重要的是这些测试文档从来都是和代码同步的,我们不用再去编写一个介绍文档,而且在每次代码进行修改时还不能忘了去更新它。
编写更加健壮的代码
如果没有测试集,那么软件的开发将会导致很多额外的错误。一个错误发现之后,可能引起这个错误的地方和发现这个错误的地方相差十万八千里。你都不知道该去找谁,然后你就只能自己不知道花上多少时间来解决这个问题。如果我们采用测试驱动开发,当我们测试出现错误的时候我们的错误只会限定在一个很小的地方,这个时候对代码进行修改不仅对程序员更加容易,而且对身心也更加健康。。
如果你算一算开发一个软件所用的时间,通常情况下测试驱动开发总是最快的。
但是测试驱动开发也有时候会出现问题,有时候我们在编写测试文档时候,对环境的配置并不总是那么容易的,比方说我们的代码和LDAP或者SQL服务器进行交互的时候,写一篇测试文档可能是一个非常困难的事情。