这是一张影响图:
当压力越大时,所做的测试就会越少。测试越少,犯的错就会越多,就会感到更大的压力。这是一个会造成情境越来越糟的循环。
我们用事先编写的测试来驱动开发,因为测试先于开发,所以我们在感到压力时,就运行这些测试,它们会马上给我们一种系统良好的感觉,而且会减少开发出错的次数,进而减少我们的压力,从而跳出上面的循环。
写一张包含所有要编写的测试清单,可以记录在一张纸上。这张表记录的就是我们要去实现的测试。
在编写要被测试的代码之前也编写测试代码。
从测试完成时能够通过的断言(形如 assertEquals()
)开始编写测试代码。
使用让人容易理解的数据,记住,你写的代码以后会有人阅读、有人维护的!
在以下的情况下,使用真实世界中的数据很有效:
让测试包含预期和实际的结果,努力让它们容易理解,因为还要让其他人阅读的,所以要留下尽可能多的线索。
使用一个公开的 API 接口或者包时,可以编写一个测试来验证这个 API 工作是否符合我们的期望。这样做有两点好处:
* 可以让我们更好地理解它。
* 如果测试无法运行,可能是使用的问题,也可能是 API 本身的问题,测试会让问题更快地暴露出来。
当你感觉到累的时候,休息一下。适当的放松会让大脑的思维得到解放。
保持不可运行-》可运行-》重构的节奏对可持续的成功非常重要。付出额外的努力来保持这种节奏是值得的。当之前的测试代码突然要求几处变化时,可能的原因是我们之前所写的测试代码太大了,这就需要重构把这些测试代码,尝试把它变成几个子测试代码咯。
如果一个测试依赖于昂贵而且复制的资源对象,那么就创建一个这些资源的模拟对象。典型的例子是数据库,建立它很耗时间,而且它也是开发过程中错误产生的温床。
解决办法是在大多数时间里不使用真正的数据库,而是写一个像数据库一样的对象,或者采用一个第三方开源的数据库模拟 API,让它仅仅驻留在内存中。这样做,不仅带来了高效的性能,而且还很可靠,可读性也很好。
如果担心模拟对象与现实对象的行为不一致,那么可以使用对实际对象适用的一系列测试来测试模拟对象,从而减少这种风险。
抛出一个异常对象来测试代码中处理错误的逻辑。因为我们的安全假设是,没有被测试过的代码是不会正常工作的,处理错误的逻辑也是代码的一部分,所以我们也要进行测试。
假设要测试当文件系统满了以后,系统会发生什么。可以花费很多时间创建许多大文件来填满整个文件系统,这种方法费时费力。另一种方法是采用伪实现,即抛出一个异常:
public class FullFile extends File {
public FullFile(String path){
super(path);
}
public boolean createNewFile() throws IOException {
throw new IOException();
}
}
如果在试图提交时,发现有许多测试没有通过。最可能的原因是你可能对刚刚编码的东西没有完全理解,因此最简单的办法就是把之前做过的工作推倒重来。
因为许多测试没有通过,注释掉一些测试代码是严格禁止的,这是一种不负责任的、偷懒的表现。
测试不能通过的时候,先返回一个常量,让测试运行通过。然后通过重构把这个常量逐步转换为用变量表示的表达式。伪实现就像登山时在头顶上方钉一个登山用的钢锥,它会让你安心,让你觉得继续爬上去是安全的。
使用伪实现有两个主要原因:
三角法的含义是:只有当我们有有了两个或两个以上的测试用例时,才能对功能进行抽象。
只有在不能确定是否需要对要实现的功能是否已经进行了正确的抽象时,才使用这个方法。
显明实现含义是:如果我们知道要些什么,并且能够很快地完成,那就直接实现吧。
显明实现只是第二选择。因为你在追求自身的完美,而这是不可能的!所以记住时刻保持不可运行-》可运行-》重构的编码习惯!
写一个布尔表达式传递给一个断言,来判断我们的工作是否运行正常。形如assertEquals(...)
在 JUnit 中,可以为断言传递更详细的信息,形如 assertTrue("测试用例 1",false)
。
如果几个测试都存在一些通用对象,这种重复是不好的,因为:
所以我们把几个测试都需要的通用对象,转变为实例变量,然后在 setUp()
中初始化这些对象。
使用 tearDown
来释放资源,这样能避免重复,原因和初始化固定设施是一样的!
不论测试代码中发生了什么,甚至是抛出一个异常,xUnit 也能保证最后调用 tearDown
。
我们需要测试期望的异常,只有捕获到这些异常,异常测试才算通过。
我们只关心捕获我们所期望的异常,因此,如果抛出了一个非期望的异常,我们也会从 xUnit 框架中得到通知。
可以把所有的测试合成一个测试套件,每个包一个套件,一次性执行所有的测试。
一般经过一段时间的适应,开发人员都会倾向于采用小步骤进行开发。可以利用 IDE 的自动重构功能来提高重构的速度。
应该测试这些东西:
记住,只测试我们编写的代码,除非有足够的理由,否则不要测试其他来源的代码。
这些是设计存在的缺陷的特征:
如果存在两个测试互为冗余,根据以下原则进行删除:
LifeWare 的一个跟保险有关项目就是采用 TDD 进行开发的。共历时 4 年,有 40 人参与了这个项目。编写了大约 500,000 行代码(其中有一半是测试代码),有 4000 个能够在 20 分钟内能够执行完毕的测试。所以现在有信心了吧 O(∩_∩)O~