第二章 单元测试的基本概念和核心技法
2.1 良好的单元测试——定义
我们已经了解了程序员需要单元测试,下面我们来给单元测试作一个完整的定义:
● 定义: 单元测试是一段自动执行的代码,它调用被测类或被测方法,然后验证关于被测类或被测方法逻辑行为的假设确实成立。单元测试几乎总是用单元测试框架(unit testing framework)来写就的,单元测试是易于写就、执行快速、完全自动化、值得依赖、易于阅读并易于维护的。
这个定义有点长,但是它却包含了大量重要信息:
● 单元测试的测试重点是被测类或被测方法的逻辑行为。所谓“逻辑行为”,指的是含有诸如判断、循环、选择、计算或其他决策过程的代码。
● 单元测试框架是辅助程序员写就单元测试的利器。
● 单元测试的代码本身,同被测代码一样,也应该是值得依赖、易于阅读并易于维护的。
有了单元测试的定义,我们来看看有关单元测试的几个基本概念,这些基本概念会在后面的章节中反复出现。
● 被测类(Class Under Test)和被测方法(Method Under Test):顾名思义,就是测试代码所操练的类或方法。
● 测试类(Test Fixture)和测试方法(Test Method):负责操练被测类和被测方法。Test Method一般都是Test Fixture的成员函数。
● 测试运行器(Test Runner):负责自动执行测试类中的测试方法。Test Runner可以是命令行界面的,也可以是GUI的。
2.2 进行单元测试的核心技术和核心手法
2.2.1 核心技术
前面已经讲到,单元测试所针对的目标是“单个”类或“单个”方法(这里的“方法”指C++中的自由函数)。这表明我们在单元测试中要做的主要任务是创建出被测类的对象,并调用该对象的被测方法。但是我们都知道,一个类几乎不可能完全不依赖于其他类。这种类之间的依赖会导致我们无法顺利地将一个被测类纳入单元测试覆盖这下,因为单元测试需要的是对被测类这一个类的测试,而不是同时测试被测类和它的合作者类。
因此,我们在把ClassUnderTest纳入单元测试时,也就必须先把ClassUnderTest与CollaboratorClass之间的依赖“打破”,这个过程称为“解依赖”(dependency-breaking)。解依赖就是进行单元测试的核心技术。解依赖的目标是希望能把不可控的CollaboratorClass替换成由我们控制的伪合作者类(FakeCollaboratorClass),并使被测类能方便地选择是依赖于真实的合作者类还是伪合作者类。对于产品代码,被测类依赖的是真实的合作者类,而在单元测试中,被测类依赖的是由我们控制的伪合作者。
在单元测试代码中使用可控的FakeCollaboratorClass,这给我们带来了两个便利:
● 我们可以通过FakeCollaboratorClass向ClassUnderTest返回任何我们指定的结果。
● 我们可以通过FakeCollaboratorClass来感知ClassUnderTest所做的动作。
这实际上就是FakeCollaboratorClass的两种表现形式:
● Stub:用于向ClassUnderTest返回我们指定的结果。
● Mock:用于感知ClassUnderTest的行为。
我们明白了“解依赖”是单元测试的核心技术。那么具体怎样实现解依赖呢?下面我们就来介绍4种相关的核心手法,其中前2种与CollaboratorClass有关,后2种与ClassUnderTest有关,这4种手法对于解决绝大多数的解依赖问题都适用。