测试对象是否使用正确的参数值进行实例化

时间:2014-05-09 08:19:26

标签: c# unit-testing moq

我正在使用Moq和Nunit对C#代码进行单元测试,我想测试下面的简化方法。在单元测试方面,我认为我不应该检查foo值,因为FooClass可能会发生变化,并且随后会破坏我的测试。所以我能做的最少就是验证一切都被初始化并正确调用,也就是说,someContainer获取正确的值,而FooClass获取someContainer而不是其他一些MyContainer实例。我怎么做?这种(单位)测试方法是否正确?

public MyClass
{
   ...
   public bool MyMethod(int a, int b, int c, out FooClass foo)
   {
      var someContainer = new MyContainer{
         A = a,
         B = b,
         C = c
      };

      foo = new FooClass(someContainer,1,2,3);

      ...
      return true;
   }
   ...
}

2 个答案:

答案 0 :(得分:2)

您无法测试foo获取someContainer,因为someContainer只能从方法内部访问。对该方法的适当测试是foo在正确的位置包含abc,并且它包含一些MyContainer对象。它将是FooClass构造函数的单元测试,它测试FooClass对象包含正确的MyContainer对象。

如果您的FooClass会发生变化,那么您的MyMethod也可能会发生变化,因此MyMethod的任何测试都必须改变。它会破坏您的测试这一事实很好,因为这意味着您需要查看该方法并确保该方法能够满足您的需求,并且您正在测试它是否符合您的要求。你现在应该为现在的方法编写适当的测试,或者在FooClass完成之前不要再编写任何测试。

答案 1 :(得分:1)

  

这种(单位)测试方法是否正确?

如果您想要模拟依赖项,以便可以测试代码的特定单元(例如示例中的MyClass),那么您需要考虑将代码编写到需要注意的服务中不同的责任。在您的示例中,MyMethod有几项责任:

  1. 制作新的MyContainer
  2. 制作新的FooClass
  3. 提供MyContainer实例,FooClass应使用。
  4. 创建。{/ 1}

    为了满足您测试MyMethod将特定MyContainer传递到FooClass的愿望,您可以将责任1和2分成新的课程。通常,在C#中,您将为这些类提供一个接口,并使MyClass将每个接口作为依赖项。 E.g。

    public interface IFooClassProvider
    {
        FooClass CreateFooClass(MyContainer container, int d, int e, int f);
    }
    
    public interface IMyContainerProvider
    {
        MyContainer CreateMyContainer(int a, int b, int c);
    }
    
    public MyClass
    {
        private IMyContainerProvider _myContainerProvider;
        private IFooClassProvider _fooClassProvider;
    
        public MyClass(IMyContainerProvider myContainerProvider, IFooClassProvider fooClassProvider)
        {
            _myContainerProvider = myContainerProvider;
            _fooClassProvider = fooClassProvider;
        }
        ...
        public bool MyMethod(int a, int b, int c, out FooClass foo)
        {
           var someContainer = _myContainerProvider.CreateMyContainer(a,b,c);
    
           foo = _fooClassProvider.CreateFooClass(someContainer,1,2,3);
    
           ...
           return true;
        }
        ...
    }
    

    希望&#39;真实&#39;接口的实现是显而易见的。为了执行测试,您可以为每个接口创建模拟,例如new Mock<IFooclassProvider>()并将其传递给MyClass的构造函数。在单元测试中,您可以Setup IMyContainerProvider模拟提供您在单元测试中创建的特定容器实例。然后,您可以Verify使用特定容器调用IFooClassProvider模拟方法。

    E.g。

    var mockFooClassProvider = new Mock<IFooClassProvider>();
    var mockMyContainerProvider = new Mock<MyContainerProvider>();
    
    var myKnownContainer = new MyContainer(...);
    mockMyContainerProvider.Setup(m => m.CreateMyContainer(It.IsAny<int>, ...))
        .Returns(myKnownContainer);
    
    var myClass = new MyClass(mockMyContainerProvider.Object, mockFooClassProvider.Object);
    
    FooClass fooClass;
    myClass.MyMethod(..., out fooClass);
    
    mockFooClassProvider.Verify(m => m.CreateFooClass(myKnownContainer, It.IsAny<int>(),...);
    

    对于这样一个简单的情况,这个答案正在把事情推向极端,但我认为问题中的例子是一些更复杂的代码的简化版本。以这种方式分离责任可以快速将代码简化为可测试的短片段。