Search code examples
autofixture

Register custom ISpecimenBuilder only to a particular type


I have a custom specimen builder for AutoFixture that omits requests for anonymous values in properties based on the type of the property being requested.

    public class PropertyTypeExclusion<T> : ISpecimenBuilder
    {
        public object Create(object request, ISpecimenContext context)
        {
            var propertyInfo = request as PropertyInfo;

            if (propertyInfo?.PropertyType == typeof(T))
            {
                return new OmitSpecimen();
            }

            return new NoSpecimen();
        }
    }

I can add the customization to the fixture no problem, and it works as expected.

_fixture.Customizations.Add(new PropertyTypeExclusion<IdentityRef>());

Now I want this exclusion to be registered to requests for a specific type. Something like this:

_fixture.Customize<Release>(c => new PropertyTypeExclusion<IdentityRef>());

While the use of .Customize<Release> is valid, it has the same outcome as the call to .Customizations.Add.

Is there a way to register this ISpecimenBuilder only to a specific requested type?


Solution

  • If I understand you correctly, you'd like to be able to do smth like this:

    fixture.Create<Release>();       // the `IdentityRef` prop is omitted
    fixture.Create<SomeOtherType>(); // the `IdentityRef` prop is NOT omitted
    

    Ok, let's look on this statement:

    While the use of .Customize is valid, it has the same outcome as the call to .Customizations.Add

    ... meaning that even if you register the customization for the Release type only, the other types are impacted as well:

    fixture.Customize<Release>(c => new PropertyTypeExclusion<IdentityRef>());
    fixture.Create<Release>();       // the `IdentityRef` prop is omitted which is OK
    fixture.Create<SomeOtherType>(); // the `IdentityRef` prop is also omitted but it MUST NOT
    

    That sounds like a bug in AF to me and I'd address it here...

    As a quick workaround to your issue, you can extend the customization to accept both class and property types to perform more precise filtering:

    public class PropertyTypeExclusion<TType, TProp> : ISpecimenBuilder
    {
        public object Create(object request, ISpecimenContext context)
        {
            var propertyInfo = request as PropertyInfo;
    
            if (propertyInfo?.PropertyType == typeof(TProp) &&
                propertyInfo.DeclaringType == typeof(TType))
            {
                return new OmitSpecimen();
            }
    
            return new NoSpecimen();
        }
    }
    

    Now:

    fixture.Customizations.Add(new PropertyTypeExclusion<Release, IdentityRef>());
    fixture.Create<Release>();       // the `IdentityRef` prop is omitted
    fixture.Create<SomeOtherType>(); // the `IdentityRef` prop is NOT omitted