I'm doing unit testing for C# code with Moq and Nunit and I want to test the simplified method below. In terms of unit testing, I think I shouldn't check the foo value because FooClass might be subject to change and subsequently break my test in the future. So the least I could do is verify that everything gets initialized and called correctly, that is, someContainer gets the correct values and FooClass gets someContainer and not some other MyContainer instance. How do I do that? Is this (unit) testing approach correct?

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;
   }
   ...
}
有帮助吗?

解决方案

You can't test that foo gets someContainer because someContainer is only accessible from inside the method. An appropriate test for that method would be that foo contains a, b and c in the right places and that it contains some MyContainer object. It would be a unit test of that FooClass constructor that tested that the FooClass object contained the correct MyContainer object.

If your FooClass is going to change then presumably your MyMethod is going to change too, so any tests of MyMethod would have to change too. The fact that it breaks your test is good because it means that you need to look at that method and make sure that the method does what you want and that you're testing that it does what you want. You should either write proper tests now for the method as it is now or don't bother writing any tests at all until FooClass is finalised.

其他提示

Is this (unit) testing approach correct?

If you're wanting to mock out dependencies so you can test a specific unit of your code (e.g. MyClass in your example), then you need to look at composing your code into services that take care of the different responsibilities. In your example your MyMethod has several responsibilities:

  1. Creating a new MyContainer
  2. Creating a new FooClass
  3. Providing the MyContainer instance that FooClass should be created with.

To satisfy your desire to test that MyMethod passes a particular MyContainer into the FooClass you could separate responsibilities 1 and 2 out into new classes. Typically, in C#, you would give these classes an interface and have MyClass take each of these interfaces as a dependency. 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;
    }
    ...
}

Hopefully the 'real' implementations of the interfaces are obvious. In order to perform your test, you can then create mocks for each of these interfaces, e.g. new Mock<IFooclassProvider>() and pass them into the constructor for MyClass. In your unit test you can Setup the IMyContainerProvider mock to provider a specific container instance that you create in your unit test. You can then Verify that the IFooClassProvider mock's method was called with the specific container.

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

This answer is taking things to an extreme for such a simple situation, but I figure the example in the question was a simplified version of some more complex code. Separation of responsibilities in this fashion quickly simplifies code into short testable segments.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top