关于自动化测试的一件事是,它要求您编写可测试的代码。这本身并不是一件坏事(事实上,这是件好事,因为它劝阻通常应避免的许多实践),但是,如果您尝试对现有代码进行单元测试,则可能不是以可测试的方式编写。
单例,静态方法,注册表,服务定位器等之类的东西都引入了很难模拟的依赖项。违反Demeter定律意味着您的代码库太多部分对代码库其他部分的功能了解得太多,从而引入了其他难以破解的依赖。所有这些事情使得很难将模块与其余代码库隔离开来,并且如果您不能单独测试模块,那么单元测试将失去很多价值。如果测试失败,是因为被测单元发生故障,还是由于其依赖项之一发生故障,或者是因为通过从属数据源提取的数据不是测试编写者所期望的?如果你可以的话'
我见过的大多数未考虑到单元测试的代码库本质上都是不可测试的,因为编码人员倾向于专注于使代码按他们期望的那样工作,而不是进行保持松散耦合和依赖关系明确的必要工作。考虑到单元测试而编写的代码看起来会非常不同。
很多人在第一次开始进行单元测试时就采取了幼稚的方法,他们认为他们可以为现有的代码库编写大量的测试,但是一切都会很好,但是由于上述问题。他们开始发现必须在单元测试中进行大量设置才能完全运行它们,并且结果常常令人质疑,因为代码中缺乏隔离性意味着您无法追踪导致测试失败的原因。他们还倾向于尝试编写“聪明的”测试,这些测试演示了系统如何工作的高度抽象的方面。这往往会失败,因为“聪明”的单元测试本身就是潜在的错误源。测试是否由于测试模块中的错误而失败,还是由于测试中的错误?测试应该非常简单,以至于不可能有任何漏洞隐藏在其中。实际上,最佳测试很少会超过2行,第一行指示被测单元执行某项操作,第二行则断言所执行的操作是预期的。
如果您的团队认真考虑采用单元测试,那么从现有项目开始是不明智的。如果不进行重大重构,您团队的现有项目可能无法测试。最好使用一个新项目作为学习单元测试的基础,因为您可以使用一个干净的平台。您可以设计新的代码库,以使依赖注入优于单例,注册表和其他此类隐藏的依赖关系,您可以将其编写为依赖接口而不是实现等。您还可以(并且应该)与要测试的代码一起编写测试,因为事后编写测试会导致单元测试,以确保被测试的模块执行了您认为可能要执行的操作,而不是那些测试了它可以执行的操作规格说明应该做什么。
一旦您对单元测试有了信心,您的团队就可能开始意识到其现有代码中的缺陷,这些缺陷将成为单元测试的障碍。这是您可以开始重构现有代码以使其更具可测试性的时候。不要雄心勃勃,立即尝试做所有这些事情,或者尝试替换一个可以使用全新系统的系统,只需从找到易于测试的代码库中开始(那些没有任何依赖项或存在明显依赖项的地方),并为它们编写测试。我知道我说过与代码一起编写测试比之后编写测试更可取,但是即使后来编写的测试也具有作为起点的价值。编写测试就好像您对类的工作方式一无所知,除了其规范要求的内容外。当您运行测试并获得失败时,则说明或实现均不正确。仔细检查两者,以确定哪个是错误的,然后相应地更新测试或代码。
摘下低挂的水果后,您的实际工作就开始了。您需要开始在代码库中查找隐藏的依赖项,并一次对其进行更正。在这一点上不要过于雄心勃勃,只需要一次执行一个模块,或者甚至在一个模块中只执行一个问题,直到解决测试障碍并继续下一步。
TL:DR:大多数人认为测试很容易,您可以轻松地将测试改编为现有代码。这两个假设都是错误的。如果您考虑到这两个事实而着手进行项目的单元测试,那么您更有可能成功。