AutoFixture Tips and Tricks

AutoFixture is an amazing library that simplifies the process of creating test data in .NET. If for some reason you don't use this fantastic tool, it's time to consider it. Today, I'm going to share some tips for using AutoFixture that will be beneficial for developers of all skill levels. Let's get started!

Generate random enum members

(Boolean generator has the same behavior)

Default EnumGenerator of AutoFixture returns enum members in sequential order starting from the first element.

Example:

var fixture = new Fixture();

// Generate and print 8 colors
Enumerable.Range(1, 8)
          .Select(_ => fixture.Create<Color>())
          .ToList()
          .ForEach(x => Console.WriteLine(x));

enum Color
{
    None = 0,
    Black = 1,
    Red = 2,
    Blue = 3
}

Output:

None
Black
Red
Blue
None
Black
Red
Blue

If you need a random enum generator, it's here:

using AutoFixture.Kernel;

/// <summary>
/// AutoFixture random generator for enums
/// </summary>
public class RandomEnumGenerator : ISpecimenBuilder
{
    private static readonly Random _randomizer = Random.Shared;

    public object Create(object request, ISpecimenContext context)
    {
        var type = request as Type;

        if (type?.IsEnum != true)
        {
            return new NoSpecimen();
        }

        var values = Enum.GetValues(type);
        var index = _randomizer.Next(values.Length);
        return values.GetValue(index)!;
    }
}

Usage:

var fixture = new Fixture();
fixture.Customizations.Insert(0, new RandomEnumGenerator());

Enumerable.Range(1, 8)
          .Select(_ => fixture.Create<Colour>())
          .ToList()
          .ForEach(x => Console.WriteLine(x));

Output:

Red
Black
None
Blue
Red
Blue
Red
Blue

Generate a few customized objects

From time to time we need to generate a few objects and customize one or more properties like in the example below:

var fixture = new Fixture();
var randomizer = new Random();

// Generate a few users with customized RegisteredAt property.
var users = fixture.Build<User>()
                   .With(x => x.RegisteredAt, GetDateTimeInFuture())
                   .CreateMany()
                   .ToList();

DateTime GetDateTimeInFuture()
{
    return DateTime.Now.AddDays(randomizer.Next(10, 100));
}

public record User(int Id, string Name, DateTime RegisteredAt);

Although we expect all 3 users to have distinct values for the RegisteredAt property, the value will be the same:

Fix for this common mistake:

.With(x => x.RegisteredAt, () => GetDateTimeInFuture())
// or
.With(x => x.RegisteredAt, GetDateTimeInFuture)

Generate decimal and double values

Consider this code:

var fixture = new Fixture();

var value1 = fixture.Create<double>();
var value2 = fixture.Create<decimal>();

AutoFixture will always generate whole numbers, while we may expect something with decimal point (as long as we generate double and decimal).

Solution:

var fixture = new Fixture();
fixture.Register(() => fixture.Create<int>() * 1.33m); // for decimal
fixture.Register(() => fixture.Create<int>() * 1.33); // for double

var value1 = fixture.Create<double>();
var value2 = fixture.Create<decimal>();

Deal with circular dependencies

By default, Fixture is equipped with ThrowingRecursionBehavior, which throws exceptions whenever it needs to generate a graph of objects with a circular dependency:

var employee = fixture.Create<Employee>();

public record Employee(int Id, string Name, Department Department);
public record Department(int Id, string Name, IEnumerable<Employee> Employees);

Output:

Unhandled exception. AutoFixture.ObjectCreationExceptionWithPath: AutoFixture was unable to create an instance of type AutoFixture.Kernel.FiniteSequenceRequest because the traversed object graph contains a circular reference.

Solution:

As the description of the exception suggests — replace ThrowingRecursionBehavior with OmitOnRecursionBehavior:

var fixture = new Fixture();

// Replace ThrowingRecursionBehavior with OmitOnRecursionBehavior
fixture.Behaviors
       .OfType<ThrowingRecursionBehavior>()
       .ToList()
       .ForEach(b => fixture.Behaviors.Remove(b));
fixture.Behaviors.Add(new OmitOnRecursionBehavior());

var employee = fixture.Create<Employee>();

public record Employee(int Id, string Name, Department Department);
public record Department(int Id, string Name, IEnumerable<Employee> Employees);

This is it for today and thank you for reading. Don't forget to give a ⭐ to AutoFixture repository on GitHub, it deserved it.

Cheers!

Did you find this article valuable?

Support Aleksei Zagoskin by becoming a sponsor. Any amount is appreciated!