Reputation: 1729
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;
}
...
}
Upvotes: 0
Views: 453
Reputation: 5329
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:
MyContainer
FooClass
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.
Upvotes: 1
Reputation: 54417
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.
Upvotes: 2