支持单元测试的真实依赖关系

本文探讨了在单元测试中如何处理真实的依赖关系,指出内部依赖性在重构时可能导致测试脆弱。提出通过功能核心(纯函数)和命令式外壳(处理不确定行为)的设计,以及使用Fake对象来替换真实依赖,以提高测试的稳定性和可维护性。
摘要由CSDN通过智能技术生成

目录

前言:

为什么内部依赖性不好

功能核心

命令式外壳

Fakes

架构依赖

结论


前言:

在进行单元测试时,真实的依赖关系是一个需要特别考虑的问题。真实的依赖关系是指被测试单元所依赖的外部组件、服务或对象,它们可能难以在测试环境中进行模拟或替代。

  如果你使用过单元测试,可能已经使用过依赖注入来解耦对象并在测试它们时控制它们的行为。 可能已经将模拟或存根注入到被测系统中,以便定义可重复的、确定性的单元测试。

这样的测试可能如下所示:

  1. [Fact]
  2. public async Task AcceptWhenInnerManagerAccepts()
  3. {
  4. var r = new Reservation(
  5. DateTime.Now.AddDays(10).Date.AddHours(18),
  6. "x@example.com",
  7. "",
  8. 1);
  9. var mgrTD = new Mock<IReservationsManager>();
  10. mgrTD.Setup(mgr => mgr.TrySave(r)).ReturnsAsync(true);
  11. var sut = new RestaurantManager(
  12. TimeSpan.FromHours(18),
  13. TimeSpan.FromHours(21),
  14. mgrTD.Object);
  15. var actual = await sut.Check(r);
  16. Assert.True(actual);
  17. }

(此 C# 测试使用 xUnit.net 2.4.1 和 Moq 4.14.1。)

这样的测试很脆弱,会增加维护负担。

为什么内部依赖性不好

  正如上面的单元测试所暗示的,RestaurantManager依赖于注入的 IReservationsManager 依赖项。 这个接口是一个内部实现细节。 将整个应用程序想象成一个蓝色盒子,其中有两个对象作为内部组件:


  应用程序包含许多内部构建块。 上图强调了两个这样的组件,以及它们如何相互作用。如果想重构应用程序代码会怎样? 重构通常涉及更改内部构建块之间的交互方式。 例如,你可能想要更改 IReservationsManager 接口。当进行这样的更改时,将破坏一些依赖于接口的代码。 这是可以预料的。 毕竟,重构涉及更改代码。

  当测试还依赖于内部实现细节时,重构也会破坏测试。 现在,除了改进内部代码外,还必须修复所有损坏的测试。使用像 Moq 这样的动态模拟库往往会放大问题。 现在必须访问配置模拟的所有测试并调整它们以模拟新的内部交互。这种摩擦可能会从一开始就阻止你进行重构。 如果知道有保证的重构会给你带来很多额外的修复测试工作,你可能会认为不值得为此费心。 相反,你会让生产代码处于次优状态。

有没有更好的办法?

功能核心

  为了找到更好的替代方案,必须首先了解问题所在。 为什么首先使用测试替身(模拟和存根)?测试替身有一个主要目的:它们使我们能够编写确定性的单元测试。单元测试应该是确定性的。 多次运行测试应该每次都产生相同的结果(其他条件不变)。 在星期三成功的测试不应该在星期六失败。

  通过使用测试替身,每个测试都可以控制依赖项的行为方式。 在 《Working Effectively with Legacy Code 》中,Michael Feathers 将测试比作虎钳。 它是一种修复特定行为的工具。然而,测试替身并不是使测试具有确定性的唯一方法。更好的选择是使生产代码本身具有确定性。 例如,假设需要编写代码来计算截锥体的体积。 只要视锥体不变,体积就保持不变。 这样的计算是完全确定的。

  主要使用确定性操作来编写生产代码。 例如,代替上面的 RestaurantManager,可以使用如下方法编写不可变类:

  1. public bool WillAccept(
  2. DateTime now,
  3. IEnumerable<Reservation> exist
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值