We are using AutoFixture.AutoMoq 4.17.0.
The following code throws an exception during Create:
AutoFixture.ObjectCreationExceptionWithPath: AutoFixture was unable to create an instance from XXX.TypeWithInternalConstructor, most likely because it has no public constructor, is an abstract or non-public type.
[TestClass]
public class AutoFixtureExperiments
{
[TestMethod]
public void ShouldActivateTypesWithInternalConstructor()
{
var fixture = new Fixture();
fixture
.Behaviors.OfType<ThrowingRecursionBehavior>().ToList()
.ForEach(b => fixture.Behaviors.Remove(b));
fixture.Behaviors.Add(new OmitOnRecursionBehavior(2));
fixture.ResidueCollectors.Add(
new Postprocessor(
new MethodInvoker(
new ModestInternalConstructorQuery()),
new AutoPropertiesCommand()
));
var result = fixture.Create<TypeWithInternalConstructor>();
Assert.AreNotEqual(0, result.Property);
}
}
public class ModestInternalConstructorQuery : IMethodQuery
{
public IEnumerable<IMethod> SelectMethods(Type type)
{
if (type == null) throw new ArgumentNullException(nameof(type));
return from ci in type.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic)
where ci.IsAssembly // Take internal constructors only
let parameters = ci.GetParameters()
//where parameters.All(p => p.ParameterType != type)
orderby parameters.Length ascending
select new ConstructorMethod(ci) as IMethod;
}
}
public class TypeWithInternalConstructor
{
public int Property { get; }
public TypeWithInternalConstructor Child { get; }
internal TypeWithInternalConstructor(int property, TypeWithInternalConstructor child)
{
Property = property;
Child = child;
}
}
The code is based on @alex-povar answer in this SO thread.
When I debug the AutoFixture sources it seems that when the constructor parameters are being resolved that for the child constructor parameter an OmitSpecimen is resolved.
By the way the same exception occurs when the recursion depth is set to 1.
How can I tell AutoFixture to create an instance of a type with an internal constructor that has a self referencing parameter type in the constructor?
As far as I can tell the answer provided by Alex is correct for that specific question, as the type created in that question did not contain circular dependencies, but your code does.
In AutoFixture self-references are considered a code smell, and their use is discouraged by design.
To avoid recursion exceptions you've used
OmitRecursionBehaviorwhich normally is used when the request goes through the entire pipeline at which point AutoFixture has the opportunity to replace theOmitSpecimenresult withdefault.Since you've used both
OmitRecursionBehaviorand a custom method invoker, AutoFixture is trying to invoke the constructor of your type with([some int], [OmitSpecimen])which is obviously not a valid input for your constructor.This causes the entire resolution to fall back to aNoSpecimen, which causes the exception that says the object could not be created.Fortunately AutoFixture does have a
NullRecursionBehaviorthat immediately generates anullwhen recursion is encountered.