第二十八章 测试边界

是的,这是正确的:测试是系统的一部分,他们像系统的其他部分一样参与架构。在某些方面,这种参与是非常正常的。换句话说,它可以是非常独特的。

把测试做为系统组件

有很多困惑关于测试的。他们是系统的一部分吗?他们是否与系统分离?那里有哪些测试?单元测试和集成测试是不同的东西吗?验收测试,功能测试,黄瓜测试,TDD(测试驱动开发)测试,BDD(行为驱动开发)测试,组件测试等等呢?

讨论这场问题并不是本书的重点,幸运的是没有必要。从架构的角度来看,所有的测试都是一样的。无论是由TDD创建的小型测试,还是大型FitNesse,Cucumber,SpecFlow或JBehave测试,它们在架构上都是等效的。

测试,就其性质而言,遵循依赖规则;他们非常详细和具体;而且他们总是依赖于被测试的代码。实际上,你可以将测试视为体系结构中最外面的一个圆圈。系统中没有任何内容依赖测试,测试总是依赖系统的组件。

测试也是可独立部署的。事实上,大部分时间他们都被部署在测试系统中,而不是生产系统中。因此,即使在独立部署没有必要的系统中,测试仍将独立部署。

测试是最孤立的系统组件。它们不是系统运行所必需的。没有用户依赖于他们。他们的作用是支持开发,而不是运行。然而,它们同样不是系统组件。实际上,它们在很多方面代表了所有其他系统组件应遵循的模式。

设计可测试性

测试的极端隔离以及它们通常不被部署的事实常常使得开发者认为测试不在系统的设计之中。这是一个灾难性的观点。没有很好地整合到系统设计中的测试往往是脆弱的,并且使系统变得僵硬和难以改变。

这个问题当然是耦合。与系统强烈耦合的测试必须随着系统而改变。即使系统组件最微不足道的变化也会导致许多耦合测试中断或需要更改。

这种情况可能会变得严重。通用系统组件的更改可能导致数百甚至数千测试中断。这就是所谓的脆弱性测试问题(Fragile Tests Problem)。

不难看出这是如何发生的。想象一下,例如,一套使用GUI来验证业务规则的测试。这样的测试可以在登录屏幕上开始,然后浏览页面结构,直到他们可以检查特定的业务规则。对登录页面或导航结构的任何更改都可能导致大量测试中断。

脆弱的测试通常会使系统变得僵化。当开发人员意识到简单的系统更改会导致大规模的测试失败时,他们可能会拒绝进行这些更改。例如,想象开发团队和营销团队之间的对话,他们要求对导航结构进行简单的更改,这将导致1000次测试中断。

解决方案是设计可测试性。软件设计的第一条规则(无论是可测试性还是其他原因)总是相同的:不要依赖于易变的东西。GUI是不稳定的。通过GUI操作系统的测试套件必然是脆弱的。因此设计系统和测试应满足,可以在不使用GUI的情况下测试业务规则。

测试API

完成这个目标的方法是创建一个特定的API,测试可以用来验证所有的业务规则。这个API应该有超级的能力,允许测试避开安全约束,绕过耗时的资源(比如数据库),强制系统进入特定的可测试状态。此API将是用户界面使用的一组交互器和接口适配器的超集。

测试API的目的是将测试与应用程序分离。这种解耦不仅仅包括从UI中分离测试:目标是将测试的结构与应用程序的结构分离。

结构耦合

结构耦合是测试耦合中最强,最隐蔽的形式之一。设想一个测试套件,它对每个生产类都有一个测试类,对每个生产方法都有一套测试方法。这样的测试套件与应用程序的结构紧密相关。

当这些生产方法或类之一改变时,大量的测试也必须改变。因此,测试是脆弱的,他们使生产代码僵化。

测试API的作用是从测试中隐藏应用程序的结构。这允许生产代码被重构和演变,不会影响测试。它也允许测试被重构并且演变以不影响生产代码的方式。

这种演变的分离是必要的,因为随着时间的推移,测试往往变得越来越具体和有针对性。相反,生产代码越来越抽象和一般化。强结构耦合会防止(或至少阻碍)这种必要的演变,并防止生产代码尽可能一般化且灵活。

安全

测试API的超级的能力如果部署在生产系统中可能会很危险。如果这是一个值得关注的问题,那么测试API及其实现的危险部分应该保存在一个单独的,可独立部署的组件中。

小结

测试不在系统之外;相反,它们是系统的一部分,如果要提供稳定性和回归所需的好处,就必须设计好。没有被设计成系统一部分的测试往往是脆弱的,难以维护。这样的测试像已经到了维护的底线被丢弃,因为它们太难维护。1

1. Such tests often wind up on the maintenance room floor—discarded because they are too difficult to maintain.

results matching ""

    No results matching ""