静态方法和单元测试

时间:2010-11-18 23:45:20

标签: c++ unit-testing

我一直在阅读使用TDD时往往会避免使用静态方法,因为它们往往难以模拟。我发现,单元测试最简单的方法是一个具有简单功能的静态方法。不必实例化任何类,鼓励简单,做一件事,“独立”等方法。

有人可以解释TDD最佳实践与实用轻松之间的差异吗?

感谢, 甲

4 个答案:

答案 0 :(得分:14)

静态方法很容易测试,但是直接调用静态方法的东西通常不容易独立于它依赖的静态方法进行测试。使用非静态方法,您可以使用存根/模拟/假实例来简化测试,但如果您正在测试的代码调用静态方法,则它实际上与该静态方法“硬连线”。

答案 1 :(得分:3)

问题的答案是,在我看来,“面向对象似乎是TDD人们所想的全部内容”。

为什么呢?我不知道。也许他们都是Java程序员,他们已经感染了使一切依赖于六个间接层,依赖注入和接口适配器的疾病。

Java程序员似乎喜欢预先让一切变得困难,以便“以后节省时间。”

我建议在您的TDD中应用一些敏捷原则:如果它没有引起问题,那么就不要修复它。不要过度设计。

在实践中,我发现如果首先测试静态方法,那么它们不会成为调用者中的错误的原因。

如果静态方法快速执行,那么它们就不需要模拟。

如果静态方法使用程序外部的东西,那么您可能需要一个模拟方法。在这种情况下,您需要能够模拟许多不同类型的函数行为。

如果你确实需要模拟静态方法,请记住有办法在OO编程的

例如,您可以编写脚本来将源代码处理为调用mock函数的测试表单。您可以将具有不同版本功能的不同目标文件链接到测试程序中。您可以使用链接器技巧来覆盖函数定义(如果它没有内联)。我相信还有一些我没有在这里列出的技巧。

答案 2 :(得分:2)

测试静态方法很容易。问题是在测试其他代码时无法将其他代码与静态方法隔离开来。调用代码与静态代码紧密耦合。

对静态方法的引用不能被许多模拟框架嘲笑,也不能被覆盖。

如果你有一个正在进行大量静态调用的类,那么要测试它,你必须为所有这些静态调用配置应用程序的全局状态 - 所以维护变成了一场噩梦。如果您的测试失败,那么您不知道哪个位代码导致了失败。

错误,是许多开发人员认为TDD无意义的原因之一。他们为测试结果付出了巨大的维护费用,只是模糊地指出出了什么问题。如果他们只减少了他们的代码单元之间的耦合,那么维护将是微不足道的,并且测试结果是特定的。

答案 3 :(得分:0)

这个建议在大多数情况下都是正确的......但并非总是如此。我的评论不是特定于C ++的。

  1. 编写静态方法的测试(纯/无状态函数):即输出的工作以产生一致的结果。例如在下面添加 - 在给定特定输入集的情况下,将始终给出相同的值。在为这些或代码调用这些纯静态方法编写测试时,没有问题
  2. 消耗静态的静态方法编写测试:例如GetAddCount()如下。在多个测试中调用它可以产生不同的值。因此,一项测试可能会损害另一项测试的执行 - 测试需要独立。所以现在我们需要引入一种方法来重置静态状态,这样每个测试都可以从一个干净的平板开始(比如像ResetCount()这样的东西)。
  3. 为访问静态方法的代码编写测试没有对依赖项的源代码访问:再次依赖于静态方法本身的属性。但是,如果它们很粗糙,那么你就会有一种困难的依赖。如果依赖项是一个对象,那么您可以将一个setter添加到依赖类型,并为您的测试设置/注入一个假对象。当依赖项是静态的时,您可能需要进行大规模的重构才能使测试可靠地运行。 (例如,添加一个委托给静态方法的对象中间人依赖。现在为你的测试插入一个假中间人)
  4. 让我们举个例子

    public class MyStaticClass
    {
      static int __count = 0;
      public static int GetAddCount()
      {  return ++__count;  }
    
      public static int Add(int operand1, int operand2)
      {  return operand1 + operand2; }
    
      // needed for testability
      internal static void ResetCount()
      {
         __count = 0;
      }
    }
    
    ...
    
    //test1
    MyStaticClass.Add(2,3);        // => 5
    MyStaticClass.GetAddCount();    // => 1
    
    // test2
    MyStaticClass.Add(2,3);  // => 5
    //MyStaticClass.ResetCount(); // needed for tests
    MyStaticClass.GetAddCount();  // => unless Reset is done, it can differ from 1