In this article, we will explore how to automate test data generation, enhance productivity, improve test coverage, and reduce maintenance efforts with a single library called AutoFixture.

To download the source code for this article, you can visit our GitHub repository.

Let’s get started!

What Is AutoFixture?

AutoFixture is a powerful library for test data generation in .NET projects. Its main goal is to automate the process of creating test data by generating realistic values for our objects. It provides us with simple ways of obtaining both simple and complex data without having to define it by hand. AutoFixture also leverages reflection and customizable conventions to automatically create instances of classes with populated properties. 

Support Code Maze on Patreon to get rid of ads and get the best discounts on our products!
Become a patron at Patreon!

With AutoFixture we can remove the need for test maintenance as it automatically adapts to changes in our classes. Furthermore, it provides customization and extensibility options, allowing us to fashion its behavior to handle specific scenarios or implement specific rules during test data generation. It seamlessly integrates with popular .NET testing frameworks like NUnit and xUnit, easing its adoption and incorporation into existing testing setups.

Installing and Setting up AutoFixture

The first thing we have to do is install the NuGet package:

dotnet add package AutoFixture

Setting up the Code for Our Tests

Next, we will create the code to test out:

public class Employee
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
    public decimal Salary { get; set; }

    public Employee(string firstName, string lastName, int age, decimal salary)
    {
        FirstName = firstName;
        LastName = lastName;
        Age = age;
        Salary = salary;
    }

    public string GetFullName() => $"{FirstName} {LastName}";
}

Then, we move on to our department:

public class Department
{
    public string Name { get; set; }
    public List<Employee> Employees { get; set; }

    public Department(string name)
    {
        Name = name;
        Employees = new List<Employee>();
    }

    public void AddEmployee(Employee employee)
    {
        Employees.Add(employee);
    }

    public Employee? GetEmployee(string firstName)
    {
        return Employees
            .FirstOrDefault(e => e.FirstName == firstName);
    }

    public decimal CalculateAverageSalary()
    {
        if (!Employees.Any())
        {
            return 0;
        }

        return Employees.Average(e => e.Salary);
    }
}

Both of these classes are pretty simple to understand with straightforward logic.

Setting up AutoFixture for Test Data Generation

Let’s get AutoFixture up and running:

public class EmployeeTests
{
    private readonly IFixture _fixture;

    public EmployeeTests()
    {
        _fixture = new Fixture();
    }
}

First, we create a xUnit test project. Then we create the EmployeeTests class. It has a single private _fixture field of IFixture type, which is an interface provided by the AutoFixture library. In the constructor, we create a Fixture instance and assigned it to the _fixture field.

Those two lines are all we need as setup to start using AutoFixture.

Generating Basic Test Data With AutoFixture

We can use AutoFixture to easily generate simple reference and value types:

[Fact]
public void WhenConstructorIsInvoked_ThenValidInstanceIsReturned()
{
    // Arrange
    var firstName = _fixture.Create<string>();
    var lastName = _fixture.Create<string>();
    var age = _fixture.Create<int>();
    var salary = _fixture.Create<decimal>();

    // Act
    var employee = new Employee(firstName, lastName, age, salary);

    // Assert
    employee.FirstName.Should().Be(firstName);
    employee.LastName.Should().Be(lastName);
    employee.Age.Should().Be(age);
    employee.Salary.Should().Be(salary);
}

First, we create a test method to verify the behavior of the constructor of the Employee class. Then, we use the Create<T>() method provided by AutoFixture to assign random values.  We do that for firstName, lastName, age, and salary. The only difference is that for T, we use the concrete type we need for each variable.

Next, we proceed by creating a new Employee instance using the generated values. Finally, we assert that the properties of the created Employee object match the respective input values. We make the assertions using the FluentAssertions library, which provides a readable syntax for expressing assertions.

We can use AutoFixture to get various basic and numeric types such as bool, char, string, byte, int, double, and decimal.

But we can also use it to generate complex types as well:

[Fact]
public void WhenAddEmployeeIsInvoked_ThenEmployeeIsAddedToTheList()
{
    // Arrange
    var employee = _fixture.Create<Employee>();
    var department = _fixture.Create<Department>();
    var employeesCount = department.Employees.Count;

    // Act
    department.AddEmployee(employee);

    // Assert
    department.Employees.Count.Should().Be(employeesCount + 1);
}

We create a unit test to verify the behavior of the Department‘s class AddEmployee() method. Then we use the Create<T>() method to create instances of the Employee and Department classes. We also store the initial count of employees. After that, we invoke the AddEmployee method on the department, passing the employee variable created by AutoFixture. Finally, we assert that the count of employees in the department has increased by one, ensuring that the employee was successfully added to the list.

Generating Custom Test Data With AutoFixture

One very useful side of AutoFixture is the ability to customize our data:

[Fact]
public void WhenAddEmployeeIsInvoked_ThenEmployeeIsAddedToTheList()
{
    // Arrange
    var employee = _fixture.Create<Employee>();
    var employees = _fixture.CreateMany<Employee>(5).ToList();
    var department = _fixture.Build<Department>()
        .With(x => x.Employees, employees)
        .Create();

    // Act
    department.AddEmployee(employee);

    // Assert
    department.Employees.Count.Should().Be(6);
}

We update our AddEmployee method test by changing the logic for generating a Department object. Then we use the Build<T>() method of AutoFixture to start building an instance of our department.

We continue with the With method to specify additional configurations for the object we are building. In this case, we focus on the Employees property. The _fixture.CreateMany<Employee>(5) call generates a collection of five instances of the Employee class. As this returns an IEnumerable<T>, we use the ToList() method to convert the generated collection into a List<Employee>.

Finally, we call the Create method to finalize the creation of the Department object with our custom preferences.

Using the Build<T>() and With() methods provided by Autofixture in combination with either Create<T>() or CreateMany<T>() we can achieve a high level of customization control over our test data. It’s good to remember that the Build<T>() method only adds one-time customizations that are used for the creation of the next variable. Once we use Create() or CreateMany(), the customizations are lost.

Tips and Tricks for Generating Test Data With AutoFixture

This is by far not everything you could do with the library. Next, we’ll dive further into the various techniques and strategies that can help us enhance our test data generation process using AutoFixture.

Automatic Test Data Generation With AutoFixture

AutoFixture is so powerful that it can automatically generate values for us:

[Theory, AutoData]
public void WhenConstructorIsInvoked_ThenValidInstanceIsReturned(
    string firstName, string lastName, int age, decimal salary)
{
    // Act
    var employee = new Employee(firstName, lastName, age, salary);

    // Assert
    employee.FirstName.Should().Be(firstName);
    employee.LastName.Should().Be(lastName);
    employee.Age.Should().Be(age);
    employee.Salary.Should().Be(salary);
}

We update the constructor test of the Employee class by first changing the Fact attribute to Theory and adding another one called AutoData. Then we add all the variables we need as method parameters. Once we do this, AutoFixture takes care of the rest and we can use the variables inside our method without having to declare and initialize them. By utilizing the AutoData attribute we can further save time and effort when generating our test data.

Including or Omitting Property’s Initialization With AutoFixture

Using the library we can either include or commit certain properties from our test data, but need to make one change to our code first:

public Employee(string firstName, decimal salary)
{            
    FirstName = firstName;
    Salary = salary;
}

We add a constructor overload to our Employee class that only initializes FirstName and Salary. We do this because AutoFixture will try to create an instance of the requested object with the constructor having the least parameters. 

Now that we have a constructor that doesn’t initialize all properties, let’s see how we can skip the optional ones:

[Fact]
public void WhenAddEmployeeIsInvoked_ThenEmployeeIsAddedToTheList()
{
    // Arrange
    var employee = _fixture.Build<Employee>()
        .OmitAutoProperties()
        .Create();

    var employees = _fixture.Build<Employee>()
        .OmitAutoProperties()
        .CreateMany(5)
        .ToList();

    var department = _fixture.Build<Department>()
        .With(x => x.Employees, employees)
        .Create();

    // Act
    department.AddEmployee(employee);

    // Assert
    department.Employees.Count.Should().Be(6);
}

In the AddEmployee method tests, we don’t need the Employee to have any properties other than the FirstName and Salary initialized. They are not vital for the behavior of the method, so they can be omitted. To achieve this we chain the Built<T>(), where T is Employee, OmitAutoProperties(), and CreateMany() methods. The OmitAutoProperties() method will prevent any properties not used in the constructor from being initialized.

We can also use OmitAutoProperties() but still initialize some of the properties:

[Theory, AutoData]
public void GivenEmployeeExists_WhenGetEmployeeIsInvoked_ThenEmployeeIsReturned(
    string firstName, string lastName)
{
    // Arrange
    var employees = _fixture.Build<Employee>()
        .OmitAutoProperties()
        .With(x => x.FirstName, firstName)
        .With(x => x.LastName, lastName)
        .CreateMany(1)
        .ToList();

    var department = _fixture.Build<Department>()
        .With(x => x.Employees, employees)
        .Create();

    // Act
    var employee = department.GetEmployee(firstName);

    // Assert 
    employee.Should().NotBeNull();
    employee.LastName.Should().Be(lastName);
    employee.Age.Should().BeNull();
}

We create a test method to verify the behavior of the GetEmployee() method. In the test method, we create an employees variable and configure it using the Build<Employee>() method. We also use the OmitAutoProperties() method in combination to assign specific values for firstName and lastName.

Next, we create an Department object and configure the Employees property to return our already configured employees variable. Then we invoke the GetEmployee() method. Finally, we assert that the returned employee object is not null, its LastName matches the provided value, and its Age property is null.

The alternative to OmitAutoProperties() is the WithAutoProperties() method:

[Fact]
public void WhenCalculateAverageSalaryIsInvoked_ThenAccurateResultIsReturned()
{
    // Arrange
    var employees = _fixture.Build<Employee>()
        .WithAutoProperties()
        .CreateMany(5)
        .ToList();

    var department = _fixture.Build<Department>()
        .With(x => x.Employees, employees)
        .Create();

    // Act
    var averageSalary = department.CalculateAverageSalary();

    // Assert
    averageSalary.Should().BeGreaterThan(0);
}

We test for the CalculateAverageSalary() method, we use AutoFixture to create a Department object. For its Employees property, we use the employees variable which we configure by using WithAutoProperties() to state that we want all properties for each Employee to be generated as well. Finally, we assert that the average salary is greater than 0.

AutoFixture Integration With Moq

Often, our classes have constructor parameters and depend on abstractions such as abstract classes and interfaces.

Let’s update our Department class to illustrate this:

public abstract class Manager
{
    public string? Name { get; set; }
}

First, we define a Manager class that is marked as abstract and has a Name property of string type.

Next, we create another abstraction:

public interface IPayrollService
{
    void PaySalaries(IEnumerable<Employee> employee);
}

We declare the IPayrollService interface. It has a PaySalaries() method that takes in an IEnumerable of Employee objects and is responsible for paying the salaries of those employees.

Finally, let’s update our Department:

private readonly IPayrollService _payrollService;

public string Name { get; set; }
public Manager Manager { get; set; }
public List<Employee> Employees { get; set; }

public Department(
    string name,
    Manager manager,
    IPayrollService payrollService)
{
    Name = name;
    Manager = manager;
    Employees = new List<Employee>();
    _payrollService = payrollService;
}

We create a field for the IPayrollService interface and a property for the Manager abstract class. For the last step, we update our constructor accordingly. 

If we run our tests now, they will fail as AutoFixture can’t sort the dependencies on its own. To solve this, we can install and configure the AutoFixture.AutoMoq package:

public DepartmentTests()
{
    _fixture = new Fixture()
        .Customize(new AutoMoqCustomization()
        {
            ConfigureMembers = true
        });
}

In the DepartmentTests constructor, we update the AutoFixture initialization by adding customization. We do that by using the Customize method and passing a new instance of the AutoMoqCustomization class. We also set ConfigureMembers property to true, which initializes any members of the abstract types it can, but this part is optional and we can skip it.

Finally, let’s update one of our test methods:

[Fact]
public void WhenCalculateAverageSalaryIsInvoked_ThenAccurateResultIsReturned()
{
    // Arrange
    var employees = _fixture.Build<Employee>()
        .WithAutoProperties()
        .CreateMany(5)
        .ToList();

    var department = _fixture.Build<Department>()
        .With(x => x.Employees, employees)
        .Create();

    // Act
    var averageSalary = department.CalculateAverageSalary();

    // Assert
    averageSalary.Should().BeGreaterThan(0);
    department.Manager.Should().NotBeNull();
    department.Manager.Name.Should().NotBeNull();
}

We update our CalculateAverageSalary test method by adding two checks. They assert that both the Manager and its Name are not null. If we decide to skip ConfigureMembers, the Manager assertions will still pass but its Name will be null.

With this simple updated setup, we can make AutoFixture even more powerful and remove the need to explicitly mock and pass our abstract types in the constructor. This, like every other feature of the package, has the sole purpose of helping us write less code and make our tests shorter and more maintainable.

Conclusion

In this article, we explored how to automate test data generation and enhance productivity, improve test coverage, and reduce maintenance efforts with a single library called AutoFixture. By leveraging reflection and customizable conventions, AutoFixture simplifies test data generation. We also used the integration of AutoFixture with the Moq library, which allows us to handle constructor dependencies and abstractions. By leveraging its features, we can enhance productivity, improve test coverage, and reduce maintenance efforts.

Liked it? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!