【问题标题】:Possible to unit test code that wasn't initially design to be tested, without changing any code?可以在不更改任何代码的情况下对最初未设计用于测试的代码进行单元测试吗?
【发布时间】:2011-05-01 08:00:54
【问题描述】:

除非代码设置为要测试,否则您无法测试代码是否被普遍接受?

一段假设的代码:

public void QueueOrder(SalesOrder order)
{
   if (order.Date < DateTime.Now-20)
      throw new Exception("Order is too old to be processed");
   ...  
}

有些人会考虑将其重构为:

protected DateTime MinOrderAge;
{
   return DateTime.Now-20;
}

public void QueueOrder(SalesOrder order)
{
   if (order.Date < MinOrderAge)
      throw new Exception("Order is too old to be processed");
   ...
}

注意:你可以想出更复杂的解决方案;涉及IClock 接口和工厂。这不影响我的问题。

更改上述代码的问题是代码已更改。代码已更改,而客户并未要求更改。任何改变都需要召开会议和电话会议。所以我处于不测试任何东西更容易的地步。

如果我不愿意/不能做出改变:这会让我无法进行测试吗?

注意: 上面的伪代码可能看起来像 C#,但这只是为了它的可读性。这个问题与语言无关。

注意:假设代码sn-p、问题、需要重构、重构是假设的。如果您对我的不满意,您可以插入自己的假设代码示例。

注意:以上假设代码是假设的。与任何代码的任何关系,无论是活的还是死的,纯属巧合。

注意:代码是假设的,但任何答案都不是。这个问题不是主观的:因为我相信有一个答案。


更新: 当然,这里的问题是我不能保证上述示例中的更改不会破坏任何内容。当然,我将一段代码重构为一个单独的方法,并且代码在逻辑上是相同的。

但我不能保证添加新的受保护方法不会偏移对象的虚拟方法表,如果这个类在 DLL 中,那么我刚刚引入了访问冲突。

【问题讨论】:

  • 您可以对任何代码进行单元测试。有些类比其他类更难测试。例如,servlet 类比 POJO 更难进行单元测试,因为 servlet 类比 POJO 依赖更多的基础设施

标签: unit-testing language-agnostic regression


【解决方案1】:

Mockito + PowerMock 用于 Mockito。

您将能够测试几乎所有内容,而无需大幅更改您的代码。但是需要一些 setter 来注入 mock。

【讨论】:

    【解决方案2】:

    您可以使用 Mock 对象框架对原始示例进行单元测试。在这种情况下,我会多次模拟 SalesOrder 对象,每次配置不同的 Date 值,然后进行测试。这避免了更改任何已发布的代码,并允许您验证有问题的算法,即订单日期不是过去太久。

    为了更好地全面了解您正在处理的依赖项以及您可以使用的语言功能,我建议您使用Working Effective with Legacy Code

    【讨论】:

      【解决方案3】:

      这类代码的问题始终是,它正在创建并依赖于大量静态类、框架类型等......

      为所有这些对象“注入”假货的一个非常好的解决方案是 Typemock Isolator(这是商业的,但值得每一分钱)。所以是的,您当然可以测试遗留代码,这些代码是在没有考虑可测试性的情况下编写的。我已经用 Typemock 完成了一个大项目,并且取得了非常好的结果。

      除了 Typemock,您还可以使用免费的 MS Moles 框架,它的功能基本相同。只是它有一个非常不直观的 API,并且更难学习和使用。

      HTH.
      托马斯

      【讨论】:

        【解决方案4】:

        如果您的代码不是为测试而设计的,那么测试起来会更加困难。在您的示例中,您必须重写 DateTime.Now 方法,这可能不是一件容易的事。

        我认为在代码中添加测试没有什么价值,或者不允许更改现有代码,那么你不应该这样做。

        但是,如果您相信 TDD,那么您应该编写带有测试的新代码。

        【讨论】:

          【解决方案5】:

          这在某些动态语言中很容易实现。例如,即使 SUT(被测系统)将其用作隐式依赖项,我也可以在 import/using 语句中挂钩并用存根替换实际依赖项。或者我可以重新定义那些符号(类、方法、函数等)。我不是说这是要走的路。东西应该重构,但写一些特性测试更容易。

          【讨论】:

            【解决方案6】:

            答案是肯定的,需要更改一些代码才能使其可测试。

            但是可能有很多代码可以在无需更改的情况下进行测试。我会先专注于为这些东西编写测试,然后在其他客户需求让您有机会以可测试的方式重构它时再为其余部分编写测试。

            【讨论】:

            • 不幸的是,重构是我们开始遇到麻烦的地方。 :(
            • @Ian - 它被称为“技术债务”。如果您尝试向客户解释这个概念,但他们不买账,那么他们将不得不应对不断增加的维护和变更请求成本以及不断降低的稳定性和性能。
            【解决方案7】:

            可以从一开始就编写代码以进行测试。如果一开始就没有考虑到可测试性,你仍然可以测试它,你可能会遇到一些困难。

            在您的假设代码中,您可以通过创建一个日期远在过去的 SalesOrder 来测试原始代码,或者您可以模拟 DateTime.Now。像您展示的那样重构代码更适合测试,但这不是绝对必要的。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-08-25
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多