I'm trying to write a test for a class that has a constructor dependency on Func<T>
. In order to complete successfully the function under test needs to create a number of separate objects of type T
.
When running in production, AutoFac generates a new T
every time factory()
is called, however when writing a test using AutoMock it returns the same object when it is called again.
Test case below showing the difference in behaviour when using AutoFac and AutoMock. I'd expect both of these to pass, but the AutoMock one fails.
public class TestClass
{
private readonly Func<TestDep> factory;
public TestClass(Func<TestDep> factory)
{
this.factory = factory;
}
public TestDep Get()
{
return factory();
}
}
public class TestDep
{}
[TestMethod()]
public void TestIt()
{
using var autoMock = AutoMock.GetStrict();
var testClass = autoMock.Create<TestClass>();
var obj1 = testClass.Get();
var obj2 = testClass.Get();
Assert.AreNotEqual(obj1, obj2);
}
[TestMethod()]
public void TestIt2()
{
var builder = new ContainerBuilder();
builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource());
var container = builder.Build();
var testClass = container.Resolve<TestClass>();
var obj1 = testClass.Get();
var obj2 = testClass.Get();
Assert.AreNotEqual(obj1, obj2);
}
CodePudding user response:
AutoMock (from the Autofac.Extras.Moq package) is primarily useful for setting up complex mocks. Which is to say, you have a single object with a lot of dependencies and it's really hard to set that object up because it doesn't have a parameterless constructor. Moq doesn't let you set up objects with constructor parameters by default, so having something that fills the gap is useful.
However, the mocks you get from it are treated like any other mock you might get from Moq. When you set up a mock instance with Moq, you're not getting a new one every time unless you also implement the factory logic yourself.
AutoMock is not for mocking Autofac behavior. The Func<T>
support where Autofac calls a resolve operation on every call to the Func<T>
- that's Autofac, not Moq.
It makes sense for AutoMock to use InstancePerLifetimeScope
because, just like setting up mocks with plain Moq, you need to be able to get the mock instance back to configure it and validate against it. It would be much harder if it was new every time.
Obviously there are ways to work around that, and with a non-trivial amount of breaking changes you could probably implement InstancePerDependency
semantics in there, but there's really not much value in doing that at this point since that's not really what this is for... and you could always create two different AutoMock
instances to get two different mocks.
A much better way to go, in general, is to provide useful abstractions and use Autofac with mocks in the container.
For example, say you have something like...
public class ThingToTest
{
public ThingToTest(PackageSender sender) { /* ... */ }
}
public class PackageSender
{
public PackageSender(AddressChecker checker, DataContext context) { /* ... */ }
}
public class AddressChecker { }
public class DataContext { }
If you're trying to set up ThingToTest
, you can see how also setting up a PackageSender
is going to be complex, and you'd likely want something like AutoMock to handle that.
However, you can make your life easier by introducing an interface there.
public class ThingToTest
{
public ThingToTest(IPackageSender sender) { /* ... */ }
}
public interface IPackageSender { }
public class PackageSender : IPackageSender { }
By hiding all the complexity behind the interface, you now can mock just IPackageSender
using plain Moq (or whatever other mocking framework you like, or even creating a manual stub implementation). You wouldn't even need to include Autofac in the mix because you could mock the dependency directly and pass it in.
Point being, you can design your way into making testing and setup easier, which is why, in the comments on your question, I asked why you were doing things that way (which, at the time of this writing, never did get answered). I would strongly recommend designing things to be easier to test if possible.