这样,测试可以注入不同、可控的DateTime值,而不用在生产代码里写定一个值。我们要是不能修改代码,可以利用Typemock Isolator等Mocking框架,模拟静态属性和方法。针对先前的代码,测试可以写成:


[TestMethod]
public void IsExpired_BeforeExpirationDate_ReturnFalse()
{
    Isolate.WhenCalled(() => DateTime.Now)
        .WillReturn(new DateTime(2000, 1, 1));

    ExpirationChecker checker = new ExpirationChecker();
    var result = checker.IsExpired();

    Assert.IsFalse(result);
}
 
  现有的遗留代码不能轻易修改,因为我们没有针对它的测试。开始测试遗留代码之后,我们能明白:代码越丑陋,测试越困难。工具可以减轻一些痛苦,但我们要努力去构建安全的环境。

  依赖关系并不是的内容……

  我们很快会遇到的另一个问题是测试维护:测试和被测试代码耦合在一起。有耦合关系,修改生产代码有可能破坏测试。要是代码修改引起测试失败,我们需要回去解决这些问题。很多开发人员害怕维护两个代码库,这种恐惧甚至会让他们干脆不进行单元测试。真正的维护工作既取决于工具,也取决于技巧。

  编写好的测试是通过实践获得的技能。编写的测试越多,我们越精于此,同时会提升测试质量,维护也越来越少。有了测试,我们有机会重构代码,这反过来又会让测试更简洁、更易读、更健壮。

  工具对实践的难易程度有极大的影响。在基础层,我们需要一个测试框架和一个Mocking框架。在.Net领域,两种框架的选择都很丰富。

  编写第一个测试的准则

  开始的时候,我们通常会试用不同的工具,来理解他们的工作原理。我们往往不会在实际的工作代码上开始编写测试。但很快要给代码编写真正的测试。有一些小提示届时会有用:

  ● 从哪里开始:一般来说,我们编写测试是针对工作代码的,无论代码是Bug修复还是新功能。对Bug修复来说,编写的测试要检查修复。对功能来说,测试应检查正确的行为。

  ● 支架:以我们掌握的知识来看,明智的做法是先添加能确保当前实现运行的测试。添加新的代码之前先写测试,因为我们希望在修改现有代码之前,能有安全的保障。这些测试被称为“特征测试”,这个术语来自Michael Feathers编写的《修改代码的艺术》。

  ● 命名:测试重要的属性是它的名字。我们一般不会去看运行通过的测试。但当它失败时,我们看的是它的名字。所以挑一个好名字,描述出场景和代码的预期结果。好名字还有助于我们定位测试里的Bug。

  ● 评审:为了增加测试成功通过的机会,编写第一个测试时我们应该和同事结对。两个人都能从实践中学习,而且我们还能立即评审测试。好对所测的内容、测试的名称达成共识,因为这会成为团队其他人员的基本模板。

  ● AAA:现代测试的结构符合AAA模式——Arrange(测试设置)、Act(调用测试里的代码)、Assert(测试通过的标准)。如果我们使用测试驱动开发(TDD),我们要先编写完整的测试,然后再添加代码。对遗留代码来说,我们可能需要换一种方式。一旦我们有一个场景和名称需要测试,那先编写Act和Assert部分。我们要不停构建Arrange部分,因为对需要准备或仿造的依赖关系,我们知道的要更多一些。然后继续这么做,直到有一个测试能够通过。

  ● 重构:一旦准备好了测试,我们可以重构代码了。重构和测试都是后天获得的技能。我们不仅要重构被测试代码,也要重构测试本身。但DRY(不要重复自己)原则不适用于测试。测试失败时,我们希望尽快修复问题,所有的测试代码好在一个地方,而不是分散在不同的文件里。

  ● 可读性:测试应该是可读的,好是人类可读。和搭档评审测试代码,看他能否理解测试的目的。评审其他测试,看看它们的名称和内容怎样与相邻的测试区分开来。一旦测试失败,需要修复它们,好还是在运行失败之前评审它们。

  ● 组织:一旦我们有了更多的测试,组织有了用武之地。测试可以在很多方面有所不同,但明显的一个是如何快速运行。有些测试可能在毫秒内运行完,而有些则需要数秒或好几分钟。和工作一样,我们都希望得到快的反馈。这是前面谈到的怎么按一定的节奏去进行。要做到这一点,你应该把测试划分一下,把快的测试和慢的测试分开运行。这能手工(努力)去做,但在.NET领域,Typemock Isolator有一个运行器,能自动按运行速度分离。

  总结

  迈出单元测试的第一步是很有挑战的。体验依赖的东西很多——语言、工具、现有代码、依赖关系和技能。只要稍稍思考,进行大量训练和实践,你能渐入测试的佳境。