【问题标题】:Do I need to test helper/setup methods?我需要测试帮助程序/设置方法吗?
【发布时间】:2009-06-17 02:48:55
【问题描述】:

所以我有一个看起来像下面这样的辅助方法:

private D GetInstanceOfD(string param1, int param2)
{
    A a = new A();
    B a = new B();
    C c = new C(a,b, param1);

    return new D(c, param2);
}

这只是一个方便的辅助方法,我可以调用它来获取我需要的特定对象,而不是记住我需要连接哪些依赖项来获取我需要的对象。

我的第一个问题是:应该测试这样的方法吗?我能想到要测试这些类型的方法的唯一原因是确保使用正确的依赖项并正确设置。

如果第一个问题的答案是肯定的,我的第二个问题是:如何?我目前正在使用 NUnit 和 RhinoMocks 并试图弄清楚如何将这种方法重构为可测试的(嗯,是否应该测试这样的东西!);依赖注入显然在这里不起作用,因为这种方法实际上会创建依赖关系!

或者使用这种方法是不好的做法,我应该做如下的事情:

D d = new (new C(new A(), new B(), "string"), 1024);

【问题讨论】:

    标签: unit-testing refactoring


    【解决方案1】:

    要回答您的第一个问题,是的,您应该测试此方法。测试此方法将比编写整篇文章花费更少的精力,并且将为您提供运输代码的验证。

    这取决于您打算使用该方法做什么。假设函数的行为合理,您可以测试以下内容

    • 如果我为第一个参数传递 null 会发生什么?
    • 如果我为第二个参数传递一个负数会怎样?
    • GetInstanceOfD 在您预期的情况下是否真的返回 D?
    • 对于给定的输入,D 是否具有正确的值。
    • 如果 C、A 和 B 实例可访问,它们会怎样?

    【讨论】:

    • 通常,我的私有方法是通过重构以保持代码 DRY 或方法小而产生的。在这种情况下,代码实际上可能在重构之前已经通过现有测试进行了测试。在那种情况下,我通常不会直接为此方法编写测试,而是依赖于现有测试和公共方法的任何新测试。情况并非总是如此,我确实对使用反射/访问器测试的私有方法进行了一些测试,但通常情况下我只测试公共接口,因此我的测试不会不必要地耦合到类的内部。
    【解决方案2】:

    当您遇到此类问题时,要记住的一件好事是让事情变得微不足道

    设置方法需要简单。拆卸方法必须是微不足道的。辅助方法可以很重要——如果它们有自己的测试集的话。

    如果您的单元测试本身变得如此复杂,需要对其进行测试,那么您做错了。

    【讨论】:

      【解决方案3】:

      首先,该方法是私有的,因此,通常我会说该方法可能不需要直接进行测试。即使它是真实代码的辅助方法,而不是测试的辅助方法,您也可能只想通过公共接口间接测试它。

      其次,如果您确实想测试它——也许无论如何——您应该考虑使用工厂来创建对象,而不是直接实例化对象。将工厂与接口结合使用,可以让您抽象出依赖关系。只需将工厂注入到包含此方法的类的构造函数中,并将其用作此方法中的属性来创建适当的对象。如果你走这条路,你可能还会发现工厂也是逻辑创建D的地方。

      如果代码是生产代码,这肯定是我会采取的方向。下面的例子:

      public class ObjectFactory 
      {
           public virtual A CreateA() { return new A(); }
           public virtual B CreateB() { return new B(); }
           public virtual C CreateC( string str ) { return new C( CreateA(), CreateB(), str );
           public virtual D CreateD( string str, int num )
           {
               return new D( CreateC( str ), num );
           }
      }
      
      
      public class Foo
      {
          private ObjectFactory ObjectFactory { get; set; }
      
          public Foo() : this(null) { }
          public Foo( ObjectFactory factory )
          {
               this.ObjectFactory = factory ?? new ObjectFactory();
          }
      
          public void Bar()
          {
              D objD = this.ObjectFactory.CreateD( param1, param2 );
          }
      }
      
      [TestMethod]
      public void TestBar()
      {
           var factory = MockRepository.GenerateMock<ObjectFactory>();
           var d = MockRepository.GenerateMock<D>();
           factory.Expect( f => f.CreateD( "abc", 10 ) ).Return( d );
      
           var foo = new Foo( factory );
      
           foo.Bar();
      
           factory.VerifyAllExpectations();
           d.VerifyAllExpectations();
      }
      

      【讨论】:

        【解决方案4】:

        你应该测试这个方法。您应该对其进行测试,因为它可能会失败;一个 XP 格言是测试所有可能破坏的东西

        现在,这并不意味着您必须为它创建新的测试;可以通过其他方法间接测试。如果它是在重构期间提取的,那么它可能已经过测试。

        如果需要,测试它的一种方法是将它提取到一个辅助类中,它不会是私有的,并将它分成几个函数以便能够注入依赖项。

        //--- don't write this, you'll probably regret it ;o)
        D d = new (new C(new A(), new B(), "string"), 1024);
        

        【讨论】:

          【解决方案5】:

          private 方法通常表明它们不需要直接测试,因为您将测试使用这些 private 方法的方法。

          该方法感觉像是属于工厂或构建器类,而不是私有方法。如果您将实例化代码隐藏为私有方法,它将无法优雅地进行测试。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2011-12-19
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-12-06
            • 2011-08-16
            • 2010-10-09
            相关资源
            最近更新 更多