7

I'm using the latest version of Autofixture, and I'd like to prevent it from filling automatically child collections.

For example, I have a class Person that has a List property. I want all properties filled, except the list.

I tried using this customization :

public class RemoveMultiples : ICustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Customizations
            .OfType<FilteringSpecimenBuilder>()
            .Where(x => x.Specification is DictionarySpecification)
            .ToList().ForEach(c => fixture.Customizations.Remove(c));
        fixture.Customizations
            .OfType<FilteringSpecimenBuilder>()
            .Where(x => x.Specification is CollectionSpecification)
            .ToList().ForEach(c => fixture.Customizations.Remove(c));
        fixture.Customizations
            .OfType<FilteringSpecimenBuilder>()
            .Where(x => x.Specification is HashSetSpecification)
            .ToList().ForEach(c => fixture.Customizations.Remove(c));
        fixture.Customizations
            .OfType<FilteringSpecimenBuilder>()
            .Where(x => x.Specification is ListSpecification)
            .ToList().ForEach(c => fixture.Customizations.Remove(c));
    }
}

But it also prevents me from using .CreateMany().

edit: I can use .CreateMany(3) and it works.

Is there a setting somewhere that could let me autofill collections only when I need to?

edit2: Class person should look like this:

[Serializable]
public class Person
{
    private ICollection<OtherClass> _otherClasses; 
    private string _something;
    public virtual ICollection<OtherClass> OtherClasses
    {
        get { return _otherClasses; }
        set { _otherClasses = value; }
    }
}

Note that it's not always a Collection, but sometimes IList

Note2: I just realized that someone also removed the Customization for IEnumerable hence why the CreateMany() doesn't create anything.

Pacane
  • 20,273
  • 18
  • 60
  • 97
  • How does the `Person` class look? – Mark Seemann Jul 31 '13 at 19:10
  • @MarkSeemann Like this ^ – Pacane Jul 31 '13 at 19:14
  • 1
    Is there any reason for that design? As a general rule, [DO NOT provide settable collection properties](http://msdn.microsoft.com/en-us/library/vstudio/dn169389.aspx). If you change the `OtherClasses` to a read-only property, wouldn't that solve the problem? – Mark Seemann Jul 31 '13 at 20:02
  • Yeah, well the reason I could give you would be "Legacy code". We're aware that this is a bad design. However AutoFixture has been used in many places with the Build.With where new lists were passed, so I doubt it would be "that" easy to change all the lists to readonly lists. – Pacane Jul 31 '13 at 20:23
  • Fair enough. Based on your *Note2*, I can't figure out if you still have an issue/question...(?) – Mark Seemann Jul 31 '13 at 20:51
  • Yeah well, I'm still wondering if there's a way of just saying "Don't populate child collections of my objects", without removing all customizations for each data structure (so Create>() and CreateMany<..>() would still work) – Pacane Jul 31 '13 at 21:44
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/34572/discussion-between-pacane-and-mark-seemann) – Pacane Jul 31 '13 at 22:59

1 Answers1

12

Here's one way to do it.

Start by implementing a SpecimenBuilder that tells AutoFixture to skip assigning a value for collection property:

public class CollectionPropertyOmitter : ISpecimenBuilder
{
    public object Create(object request, ISpecimenContext context)
    {
        var pi = request as PropertyInfo;
        if (pi != null
            && pi.PropertyType.IsGenericType
            && pi.PropertyType.GetGenericTypeDefinition() == typeof(ICollection<>))
            return new OmitSpecimen();

        return new NoSpecimen(request);
    }
}

Then encapsulate that in a Customization:

public class DoNotFillCollectionProperties : ICustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Customizations.Add(new CollectionPropertyOmitter());
    }
}

The following tests now pass:

[Fact]
public void CreatePersonWithoutFillingCollectionProperty()
{
    var fixture = new Fixture().Customize(new DoNotFillCollectionProperties());
    var actual = fixture.Create<Person>();
    Assert.Null(actual.OtherClasses);
}

[Fact]
public void CreateManyStillWorks()
{
    var fixture = new Fixture().Customize(new DoNotFillCollectionProperties());
    var actual = fixture.CreateMany<Person>();
    Assert.NotEmpty(actual);
}

[Fact]
public void CreatListStillWorks()
{
    var fixture = new Fixture().Customize(new DoNotFillCollectionProperties());
    var actual = fixture.Create<List<Person>>();
    Assert.NotEmpty(actual);
}

[Fact]
public void CreateCollectionStillWorks()
{
    var fixture = new Fixture().Customize(new DoNotFillCollectionProperties());
    var actual = fixture.Create<ICollection<Person>>();
    Assert.NotEmpty(actual);
}
Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • 1
    Thanks it works well! In case you were wondering why I wanted to do this, well it's just to speed the test suite up. Because right now with 15k+ tests I could save ~40% build time on the CI server just by omitting to build child collections in [some] tests. – Pacane Aug 01 '13 at 14:13
  • `NoSpecimen(request)` is now obsolete. Use `NoSpecimen()` instead. – nf313743 Oct 21 '22 at 16:58