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

本文关键字:参数 实例化 对象 是否 测试 | 更新日期: 2023-09-27 18:02:26

我正在用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;
   }
   ...
}

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

你不能测试foo得到someContainer,因为someContainer只能从方法内部访问。对该方法的适当测试是foo在正确的位置包含a, bc,并且它包含一些MyContainer对象。这将是FooClass构造函数的单元测试,测试FooClass对象是否包含正确的MyContainer对象。

如果你的FooClass要改变,那么你的MyMethod可能也会改变,所以MyMethod的任何测试也必须改变。它中断测试的事实是好的,因为这意味着你需要查看那个方法并确保这个方法做了你想要的,并且你正在测试它做了你想要的。你应该现在就为这个方法编写适当的测试,或者在FooClass完成之前不要费心编写任何测试。

这种(单元)测试方法正确吗?

如果你想模拟出依赖关系,这样你就可以测试代码的特定单元(例如,在你的例子中MyClass),那么你需要考虑将你的代码组合成负责不同职责的服务。在你的例子中,你的MyMethod有几个职责:

  1. 新建MyContainer
  2. 新建FooClass
  3. 提供MyContainer实例来创建FooClass

为了满足您测试MyMethod将特定的MyContainer传递到FooClass的愿望,您可以将职责1和2分离到新的类中。通常,在c#中,您将为这些类提供一个接口,并让MyClass将每个接口作为依赖项。例如

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;
    }
    ...
}

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

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>(),...);

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