我应该测试从同一测试中的Im测试中调用的方法吗?

时间:2014-02-27 13:16:35

标签: c# unit-testing

假设我有以下设置

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方法。

我这样做是否正确?

3 个答案:

答案 0 :(得分:1)

简短回答:不。

你应该一次测试一件事。

Class1的角度来看,class2 应该根据IClass2 工作,Class1不应该“考虑它”。

想象一下,您将来某个时候替换IClass2的实现 - 那么您是否希望更新与class1相关的单元测试,因为class2已更改?可能不是。

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

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

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

答案 1 :(得分:0)

没有

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

答案 2 :(得分:0)

查看隔离原则: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);
  }
}

使用arrange act assert模式,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中,并且作为单元与代码的其余部分完全隔离。

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

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