I'm trying to test a class that takes a factory (Func<T>
) and I'm using Moq and AutoFixture.
What is the best way to setup the "environment" to see if the factory has been used and how many times and what methods have been used on the returned instances?
Currently I'm Mock'ing the T
and Injecting
a Func<T>
that keeps count of all returned Mock instances:
public class SystemUnderTest {
public SystemUnderTest(Func<IMyClass> c)
{
try {
var c1 = c();
c1.Name="n1";
c1.Send();
}
catch(Exception){
var c2 = c();
c2.Name="n2";
c2.Send();
}
}
}
private Mock<IMyClass> MockFactory()
{
var m = new Mock<IMyClass>();
m.SetupProperty(mc=>mc.Name);
_returnedStubs.Add(m);
return m;
}
[Test]
public void TestSomething()
{
var fixture = new Fixture();
fixture.Inject(()=>MockFactory().Object)
var sut = fixture.CreateAnonymous<SystemUnderTest>();
Assert.That(_returnedStubs.Count,Is.Equal(1));
_returnedStubs[0].Verify(m=>m.Send(),Times.Exactly(1));
_returnedStubs[0].Verify(m=>m.Name = "n1");
}
But it feels kinda iffy/ugly to me. And I'm pretty sure that an instance variable in a test class is a dangerous thing
Since AutoFixture is able to create anonymous delegates, when asked to create an anonymous instance of SystemUnderTest
, it will also automatically provide an anonymous Func<IMyClass>
delegate, which in turn returns an anonymous instance of IMyClass
when invoked.
This means that, given this scenario:
public class SystemUnderTest
{
public SystemUnderTest(Func<IMyClass> c)
{
try
{
var c1 = c();
// ...
}
catch (Exception)
{
var c2 = c();
// ...
}
}
}
the following code:
var fixture = new Fixture();
var sut = fixture.CreateAnonymous<SystemUnderTest>();
will assign the c1
and c2
variables with anonymous instances of IMyClass
. Furthermore, if you configure AutoFixture to work as an auto-mocking container, for example using the AutoMoqCustomization
, those anonymous instances of IMyClass
will also happen to be Moq proxies:
var fixture = new Fixture();
fixture.Customize(new AutoMoqCustomization());
var sut = fixture.CreateAnonymous<SystemUnderTest>();
This information however, although useful, doesn't really help you in your particular case since you need to get a hold of the mock objects returned by the Func<IMyClass>
factory method in your test, in order to configure their behavior and make some assertions on how they've been interacted with.
The best solution, in my opinion, is to change the implementation of the factory method from Func<IMyClass>
to an interface. This way you can create a fake factory that returns different mocks of the IMyClass
interface when the Create
method is invoked multiple times in a sequence:
So, given this example:
public interface IFactory<T>
{
T Create();
}
public class SystemUnderTest
{
public SystemUnderTest(IFactory<IMyClass> factory)
{
try
{
var c1 = factory.Create();
// ...
}
catch (Exception)
{
var c2 = factory.Create();
// ...
}
}
}
You can setup your test scenario as follows:
// Given
var c1 = new Mock<IMyClass>();
var c2 = new Mock<IMyClass>();
// TODO: setup the behavior of the mock objects
var factory = new Mock<IFactory<IMyClass>>();
factory.Setup(s => s.Create()).ReturnsInOrder(c1.Object, c2.Object);
// When
var fixture = new Fixture();
fixture.Inject(() => factory.Object)
var sut = fixture.CreateAnonymous<SystemUnderTest>();
// Then
// TODO: verify the expectations on the mock objects
Note that the ReturnsInOrder
is a custom extension method that uses the Callback
method in Moq to return different values from a stubbed method when it gets invoked multiple times in a row.
One of the best ways is to create your own function and pass it and then setup expectations or state there.
int numberOfTimesUsed = 0;
myObject.Func = (x) =>
{
numberOfTimesUsed++;
Assert.IsNotNull(x); // checks if x passed was not null
};
...
Assert.AreEqual(2, numberOfTimesUsed); // checks if called twice
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With