【问题标题】:Should I test the methods that are called from the method Im testing in the same test?我应该在同一个测试中测试从方法 Im 测试中调用的方法吗?
【发布时间】:2014-02-27 21:45:29
【问题描述】:

假设我有以下设置

public interface IClass1
{
  int Result(int val);
}

public interface IClass2
{
  int Validate(int val);
}

然后我们有一个类实现这些接口之一并将另一个作为构造函数中的参数。

public class Class1 : IClass1
{
  private Class2 class2;

  public Class1(IClass2 class2)
  {
    this.class2 = class2;
  }

  public int Result(int val)
  {
      return class2.Validate(val);
  }
}

如果我随后为 Class1 的 Result 方法创建单元测试,我是否还应该通过注入 Class2 的实例在同一个单元测试中测试 Class2 的 Validate 方法,还是应该单独测试?如果我也以这种方式在同一测试中测试 Validate 方法,我是否在进行集成测试?

现在我创建了一个 Class2 的存根,它返回 Class2.Validate 的预设值,然后检查在单元测试 Class1.Return 时是否调用了 Validate 方法。

我这样做对吗?

【问题讨论】:

    标签: c# unit-testing


    【解决方案1】:

    简答:没有。

    你应该一次测试一件事。

    Class1的角度来看,class2应该按照IClass2工作Class1不应该“想”。

    想象一下,您在将来某个时间点替换了 IClass2 的实现 - 然后您是否想要更新与 class1 相关的单元测试,只是因为 class2 已更改?可能不是。

    最好将职责分开,这也是使用接口的部分原因:从Class1 的角度来看,你并不真正知道Class2 做了什么,也不知道它是如何工作的——只知道它应该实现IClass2,这就够了。

    PS:使用测试工具,例如FakeItEasy,您可以发送IClass2 的假实现,然后验证在调用 Class1 中的 Result() 方法时确实调用了该实现。

    换句话说,Class1 只是假设它给出的IClass2 的实现是值得信赖的——我们需要做的就是确保我们实际上正在使用它。

    【讨论】:

      【解决方案2】:

      看隔离原理:http://agileinaflash.blogspot.co.uk/2012/04/is-your-unit-test-isolated.html和脏混合测试http://blog.stevensanderson.com/2009/08/24/writing-great-unit-tests-best-and-worst-practises/ 并研究一些隔离框架。我在Moq 上取得了很好的成功。

      完全隔离的单元测试将只运行一种生产方法。如果您有两个类的实例一起工作,那就是集成测试。

      在你的情况下:

      public class Class1 : IClass1
      {
        private Class2 class2;
      
        public Class1(IClass2 class2)
        {
          this.class2 = class2;
        }
      
        public int Result(int val)
        {
            return class2.Validate(val);
        }
      }
      

      使用安排行为断言模式,您对 Class1 的单元测试之一将是:

      [TestMethod]
      public void Result_WithValidInput_CallsValidate
      {
          //used the MethodUnderTest_Condition_ExpectedResult naming convention
          //Arrange
          IClass2 mockClass2;
          //TODO initialise mockClass2 with an a fake object using isolation framework, to return the relevant result to stop the code from falling over during test execution.
          Class1 class1UnderTest = new Class1(mockClass2);
      
          //Act
          class1UnderTest.Result(1);
      
          //Assert
          //TODO use methods from isolation framework to assert that mockClass2.Validate() was called with the correct argument
      }
      

      Class1 单元测试的行为证明它正确调用了相关的 IClass2 方法,然后 Class2 的单元测试将证明它可以正常工作。

      这测试 Class1 如何与其成员交互,但这不是集成测试,因为它针对 IClass2 而不是 Class2 的生产代码。唯一被调用的生产代码在 Class1 中,它作为一个 unit 与其余代码完全隔离。

      虽然严格来说,你不需要测试这个,因为 Class2 是私有的,它只是 Class1.Result() 的一个实现细节,你不需要测试 如何 Result()做某事,就是做它。

      您还可以考虑将 IClass2 作为 Result 的参数而不是 Class1 ctor,这样如果所有方法都不需要它,则不会传入它。如果您使用 DI 实例化所有依赖对象,这可以为您带来性能优势。

      【讨论】:

        【解决方案3】:

        没有。

        在这种情况下的标准做法是将“模拟”(在您的情况下为 IClass2 类型)传递给您的构造函数以测试 Class1.Result。您将模拟设计为返回特定值,并测试 Result 确实返回该值。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-01-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多