I'm trying to create an object using ISpecimenBuilder:
var fixture= new Fixture();
fixture.Customizations.Add(new SkipNonPrimitiveMembersCustomization());
var person= fixture.Create<Person>(); // works as expected
var node = fixture.Create<Node>(); // only primitive type properties are set
Which is not what I want for the node object. So I tied using FromFactory method:
var fixture = new Fixture();
var node = fixture.Build<Node>()
.FromFactory(new SkipNonPrimitiveMembersCustomization())
.Create();
and I got:
Message: AutoFixture.ObjectCreationException : The decorated ISpecimenBuilder could not create a specimen based on the request: SkipNonPrimitiveMembersCustomizationTests+Node. This can happen if the request represents an interface or abstract class; if this is the case, register an ISpecimenBuilder that can create specimens based on the request. If this happens in a strongly typed Build expression, try supplying a factory using one of the IFactoryComposer methods.
Stack Trace:
NoSpecimenOutputGuard.Create(Object request, ISpecimenContext context)
Postprocessor1.Create(Object request, ISpecimenContext context) CompositeSpecimenBuilder.Create(Object request, ISpecimenContext context)
FilteringSpecimenBuilder.Create(Object request, ISpecimenContext context)
NodeComposer1.Create(Object request, ISpecimenContext context) CompositeSpecimenBuilder.Create(Object request, ISpecimenContext context)
RecursionGuard.Create(Object request, ISpecimenContext context) CompositeNodeComposer1.Create(Object request, ISpecimenContext context)
SpecimenContext.Resolve(Object request) SeedIgnoringRelay.Create(Object request, ISpecimenContext context) CompositeSpecimenBuilder.Create(Object request, ISpecimenContext context)
FilteringSpecimenBuilder.Create(Object request, ISpecimenContext context)
NodeComposer`1.Create(Object request, ISpecimenContext context) CompositeSpecimenBuilder.Create(Object request, ISpecimenContext context) RecursionGuard.Create(Object request, ISpecimenContext context)
CompositeNodeComposer1.Create(Object request, ISpecimenContext context)
SpecimenContext.Resolve(Object request)
SpecimenFactory.Create[T](ISpecimenContext context)
SpecimenFactory.Create[T](ISpecimenBuilder builder)
SpecimenFactory.Create[T](IPostprocessComposer1 composer)
My SpecimenBuilder:
public class SkipNonPrimitiveMembersCustomization : ISpecimenBuilder
{
public object Create(object request, ISpecimenContext context)
{
if (request is PropertyInfo propInfo && propInfo != null && !IsPrimitiveType(propInfo.PropertyType))
return new OmitSpecimen();
return new NoSpecimen();
}
private static bool IsPrimitiveType(Type type)
{
if (type is null) return false;
return type.IsPrimitive ||
type.IsValueType ||
type == typeof(string) ||
// handle arrays of primitive types
type.IsArray && IsPrimitiveType(type.GetElementType()) == true ||
type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>) && type.GenericTypeArguments.All(IsPrimitiveType) ||
// type is a generic type with value type arguments that are all also primitive types
type.GetGenericArguments().Any(t => t.IsValueType && IsPrimitiveType(t));
}
}
private class Node
{
public string Name { get; set; }
public int Id { get; set; }
public string[] Tags { get; set; }
public TestEnum? TestEnum { get; set; }
public DateTime? CreatedAt { get; set; }
public Node[] Children { get; set; }
}
Btw, I know I need to avoid circular reference at the end:
fixture.Behaviors.OfType<ThrowingRecursionBehavior>().ToList()
.ForEach(b => _fixture.Behaviors.Remove(b));
fixture.Behaviors.Add(new OmitOnRecursionBehavior());