DEV Community

Aleksei Zagoskin
Aleksei Zagoskin

Posted on • Originally published at zagosk.in on

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
}

Enter fullscreen mode Exit fullscreen mode

Output:

None
Black
Red
Blue
None
Black
Red
Blue

Enter fullscreen mode Exit fullscreen mode

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)!;
    }
}

Enter fullscreen mode Exit fullscreen mode

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));

Enter fullscreen mode Exit fullscreen mode

Output:

Red
Black
None
Blue
Red
Blue
Red
Blue

Enter fullscreen mode Exit fullscreen mode

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);

Enter fullscreen mode Exit fullscreen mode

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)

Enter fullscreen mode Exit fullscreen mode

Generate decimal and double values

Consider this code:

var fixture = new Fixture();

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

Enter fullscreen mode Exit fullscreen mode

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>();

Enter fullscreen mode Exit fullscreen mode

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);

Enter fullscreen mode Exit fullscreen mode

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.

Enter fullscreen mode Exit fullscreen mode

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);

Enter fullscreen mode Exit fullscreen mode

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!

Top comments (2)

Collapse
 
calebtek profile image
OLAWALE ODEYEMI EBENEZER • Edited

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

How can AutoFixture be configured to generate numeric values instead of string values ?

Collapse
 
powerz profile image
Aleksei Zagoskin

I wouldn't complicate things and go for: var value = (int)fixture.Create<Color>().