I just got a question about AutoFixture's way of dealing with enumerables, and since I suspect this is something that might surprise a few people I found it reasonable to answer in a blog post.

In short, this unit test succeeds:

var fixture = new Fixture();
var expected = fixture.CreateMany<string>();
Assert.False(expected.SequenceEqual(expected));

If this doesn't surprise you, then read the assertion again. The sequence is expected to not equal itself!

This behavior is by design.

(I've always wanted to write that.) The reason is that the return type of the CreateMany method is IEnumerable<T>, and that interface makes no guarantee that it will return the same sequence every time you iterate over it. The implementation may be a Generator.

According to its principle of Constrained Non-determinism, AutoFixture goes to great lengths to ensure that since IEnumerable<T> might be non-deterministic, it will be. This can be very useful in flushing out incorrect assumptions made by the SUT.

This behavior is carried forward by the MultipleCustomization. This unit test also succeeds:

var fixture = new Fixture()
    .Customize(new MultipleCustomization());
var expected =
    fixture.CreateAnonymous<IEnumerable<string>>();
Assert.False(expected.SequenceEqual(expected));

The behavior is the same because with the MultipleCustomization IEnumerable<T> acts as a relationship type. The class responsible for this behavior is called FiniteSequenceRelay, but AutoFixture 2.1 will also ship with an alternative StableFiniteSequenceRelay which wraps the sequence in a List<T>.

To change the default behavior you can add the StableFiniteSequenceRelay to a Fixture's customizations:

var fixture = new Fixture();
var stableRelay = new StableFiniteSequenceRelay();
fixture.Customizations.Add(stableRelay);
 
var expected =
    fixture.CreateMany<string>();
Assert.True(expected.SequenceEqual(expected));

The above unit test succeeds. Notice that the assertion now verifies that the sequence of strings is equal to itself.

Just like MultipleCustomization the StableFiniteSequenceRelay class will be available in AutoFixture 2.1, but is not availale in 2.0.

As always, it's best to encapsulate this behavior change into a Customization:

public class StableFiniteSequenceCustomization :
    ICustomization
{
    public void Customize(IFixture fixture)
    {
        var stableRelay = 
            new StableFiniteSequenceRelay();
        fixture.Customizations.Add(stableRelay);
    }
}

This makes it easier to bundle it with the MultipleCustomization if we want all the other benefits of conventions for multiples:

public class StableMultipeCustomization : 
    CompositeCustomization
{
    public StableMultipeCustomization()
        : base(
            new StableFiniteSequenceCustomization(),
            new MultipleCustomization())
    {
    }
}

With the StableMultipleCustomization the following unit test now passes:

var fixture = new Fixture()
    .Customize(new StableMultipeCustomization());
var expected =
    fixture.CreateAnonymous<IEnumerable<string>>();
Assert.True(expected.SequenceEqual(expected));

I still prefer the default behavior for its potential to act as an early warning system, but as I realize that this behavior may be problematic in some scenarios, the StableFiniteSequenceRelay provides an easy way to change that behavior.

Update (2011.8.10): StableFiniteSequenceCustomization is now part of AutoFixture and will be available in AutoFixture 2.2.


Comments

Gleb
hello Mark, first of all thanks for a great library! I've been looking for something like AutoFixture.
Can you please clarify, what do I need to do to make following scenario work:
I have two classes, Stock and StockMarket, which are in many2many relationship (each has collection of other one). I want to use AutoFixture with Fluent NHibernate persistence specification for tests, but exception tells me: Ploeh.AutoFixture.ObjectCreationException: AutoFixture was unable to create an instance of type System.RuntimeType because the traversed object graph contains a circular reference
2012-02-27 18:18 UTC
Circular references are, IMO, a design smell, but since you are attempting to work with relational data, I guess you can't change the design.

The easiest way to get around an issue like that is to customize the types to not fill in the properties in question. Something like this:

fixture.Customize<Stock>(c => c.Without(s => s.StockMarket));

fixture.Customize<StockMarket>(c => c.Without(sm => sm.Stock));
2012-02-27 19:15 UTC
Gleb
Thanks, I'll look into that
2012-02-28 05:10 UTC
Diogo Castro
Great blog post! I love the idea of making IEnumerable<T> non-deterministic. The fact that two traversals might yield different results is often overlooked by most people (me included). Luckily ReSharper reminds me when I'm iterating through an enumerable twice within the same scope, and having AutoFixture slap me across the face would be even better.

It appears that the default behaviour has changed since this post was written nearly 4 years ago. The test now fails.
If I may ask, why the change of heart?
2015-01-15 17:32 UTC

Diogo, thank you for writing. The reason for the changed behaviour was that the old behaviour caused too much confusion. Essentially, you can boild down the problem to the fact that if you compare 'two' IEnumerable<T> instances with ReferenceEquals, it could evaluate to true, and still contain different values when you start enumerating over them.

In AutoFixture 3, we did some tweaks to make the default experience with AutoFixture more intuitive, i.e. aiming for the Pit of Success. Our experience was that dynamic enumerables constantly tripped up people, including ourselves!

The old behaviour is still available as FiniteSequenceRelay, if you want to reinstate it. Just add a FiniteSequenceRelay instance to your Fixture's Customizations collection.

2015-01-15 19:11 UTC


Wish to comment?

You can add a comment to this post by sending me a pull request. Alternatively, you can discuss this post on Twitter or Google Plus, or somewhere else with a permalink. Ping me with the link, and I may add it as a comment.

Published

Monday, 18 April 2011 13:22:29 UTC

Tags



"Our team wholeheartedly endorses Mark. His expert service provides tremendous value."
Hire me!