我将就测试进行简短的公司内部讲座。你能否给我一些很好的例子,为什么所谓的 testbackdoor 模式被认为是魔鬼?我确信这是一种反模式,但我也想向同事指出这一点。
我有一种感觉 testbackdoor 会引起头痛,但是我无法真正描述为什么会这样。
我将就测试进行简短的公司内部讲座。你能否给我一些很好的例子,为什么所谓的 testbackdoor 模式被认为是魔鬼?我确信这是一种反模式,但我也想向同事指出这一点。
我有一种感觉 testbackdoor 会引起头痛,但是我无法真正描述为什么会这样。
Back Door Manipulation在您的测试和SUT使用的夹具之间创建了耦合,这意味着如果夹具的实现发生更改,那么您可能还需要更新您的测试。但是,当您重构 SUT 时,测试应该允许您确认重构没有改变 SUT 的行为。如果重构以需要您更改测试的方式更改了夹具,则无法执行此操作。
例如,假设您正在测试一个在私有字段中维护状态的类,并且您使用后门操作来修改这些字段以进行设置并检查字段的值以进行验证。然后重构该类(例如)以删除字段中的一些冗余并在字段中使用更合适的类型。您的测试现在被破坏了,您无法确保类的行为没有改变。您可以更改您的测试以使用新字段,但是您如何确定测试仍在测试相同的行为并且您没有在您的 SUT 和测试中引入错误?
在单元测试中使用时,后门操作模式支持并鼓励继续依赖外部依赖项。它违反了依赖倒置原则。这允许危险的做法,例如全局变量。它将您的测试和开发工作与外部数据库相结合。
单元测试是测试代码的一个单元,而不是系统功能的测试。它正在通过一种方法测试一条路径。它不应该尝试测试数据库。它不应该尝试测试代码的其他功能。它应该只测试您在屏幕上看到的少量代码。
想一想全局变量是什么,以及它们为什么会成为这样的问题。它们是隐藏的状态,您可以依靠其他人可以更改它们。如果有人在您程序的其他任何地方更改了该值,您的代码将采取不同的行动。因为全局在您的界面上不可见,所以想要重用您的代码的人不会知道全局的重要性。全局违反封装,因此您的代码是非模块化的。全局变量使代码难以理解、难以重用、难以测试并导致错误。
使用后门操作,您仍然可以编写一个设置全局的单元测试,运行测试,然后检查全局。该测试使全局能够继续存在。但是该测试并不能真正证明您的代码的任何内容,因为任何其他代码都可能随时更改全局。相反,应该重构代码以在接口上传递变量,遵循依赖注入模式。这样,任何试图重用您的代码的人都会知道他们必须提供那段数据。试图重用您的代码的人没有隐藏任何东西。
想想为什么依赖数据库不利于单元测试。测试系统需要有可用的数据库才能运行测试。开发人员需要有可用的数据库才能运行他们的测试和开发。到数据库的网络必须启动。数据库服务器必须具有正确的数据库、正确的模式、正确的用户、正确的存储过程、正确的表、正确的行和正确的值。用户和测试服务器必须具有有效的凭据和密码才能访问网络、服务器和数据库。开发人员的数据库和测试数据库都必须使用相同的 SQL 方言。测试需要写入访问权限才能设置测试,即使应用程序的访问权限需要是只读的。共享数据库上的测试可能会受到使用数据库和修改内容的其他人的干扰。开发人员的工作站和测试服务器必须安装正确的数据访问对象。如果其中任何一个出现问题,测试就会停止,测试驱动的开发也会停止。
这些依赖项都不能证明代码或生产环境的工作。因此,它们的设置和维护成本很高,它们无法证明正在测试实际代码,它们可以使编码停止,并且所有测试和开发始终需要可用性。
相反,代码应该使用依赖注入来传递数据库连接(或所需的数据,或其他)。单元测试工具可以注入虚假或模拟数据库连接,该连接将准确返回建立测试条件所需的数据。如果编写得当,自动化单元测试系统将完美运行,无需访问数据库。作为奖励,无需等待数据库访问,从模拟对象获取数据时,测试运行速度将提高几个数量级。当您有 50,000 个单元测试来运行每个构建时,这一点很重要。
Back Door Manipulation 的思想非常适合自动化系统测试和自动化集成测试。他们可以证明用户级功能在存在正确数据的情况下有效。他们可以证明用户级函数适当地操纵了数据库中的数据。
后门操作通常是为遗留代码编写单元测试的最有效方法。遗留代码是没有单元测试的代码,并且是许多现有项目的现实。后门操作可以帮助开发人员编写测试以确保遗留代码的持续稳定性,即使存在正在进行的修改。被保留的可能不是好的代码,但它仍然是你必须忍受一段时间的代码。
但是,当在新代码的测试驱动开发期间在单元测试中完成时,使用后门操作会鼓励进行比单元测试更大的测试。这对自动化单元测试很不利,因为它会使测试驱动的开发停止。而且它不利于代码质量和重用,因为它支持非模块化代码。