UnitTest With AutoFixture In .NET 6.0

Writing test cases is integral part of our day-to-day work. Setting up test data and iterating through big objects is also a tedious activity. In certain circumstances, we must set dummy data, since our test does not depend on these values, but our construction code does. AutoFixture is a great way to dummy data for our testing purposes.

In this tutorial, we are going to see how AutoFixture can be used to create robust test cases.

Please refer the link to learn more about AutoFixture.

Here is the definition that I have copied from the github below:

AutoFixture is an open-source library for .NET designed to minimize the 'Arrange' phase of your unit tests in order to maximize maintainability. Its primary goal is to allow developers to focus on what is being tested rather than how to setup the test scenario, by making it easier to create object graphs containing test data."

For this tutorial, I will be using the below tools:

  1. Microsoft Visual Studio Community 2022 (64-bit) - Preview
  2. Version 17.3.0 Preview 6.0
  3. .NET 6.0
  4. XUnit – please refer the link to understand the Four Phase test Pattern
  5. AutoFixture

Let us jump into a sample class and the relevant test case.

Assume that I have the below class:

namespace AutoFixtureTutorial {
    public class Employee {
        Employee(string firstName, string lastName, int employeeNumber) {
            FirstName = firstName ??
                throw new ArgumentNullException(nameof(firstName));
            LastName = lastName ??
                throw new ArgumentNullException(nameof(lastName));
            EmployeeNumber = employeeNumber;
        }
        public string FirstName {
            get;
            set;
        }
        public string LastName {
            get;
            set;
        }
        public int EmployeeNumber {
            get;
            set;
        }
        public string FullName {
            get {
                return $ "{FirstName} {LastName}";
            }
        }
    }
}

Create a Test case on Employee’s Full Name.

Please find the XUnit Test class with Test case for Full Name:

using AutoFixtureTutorial;
namespace AutofixtureTest {
    public class EmployeeFullNameTests {
        [Fact]
        public void FullName_Return_Expected() {
            var sut = new Employee("Prasad", "Raveendran", 12345);
            var actual = sut.FullName;
            Assert.Equal("Prasad Raveendran", actual);
        }
    }
}

Run this and see the output:

So far so good. Now, we are going to introduce a few more enhancements to the test cases other than the FullName.

[Theory]
[InlineData("Prasad", "Raveendran", "Prasad Raveendran")]
public void FullName_By_FirstName_LastName(string firstName, string lastName, string expected) {
    var sut = new Employee(firstName, lastName, 12345);
    var actual = sut.FullName;
    Assert.Equal(expected, actual);
}

Here we are using the [Theory] attribute for passing the properties as parameters. The [InlineData] attribute is used to set the test data.

Execute this test case and see the output:

The test case has been passed.

Now I’m going to add another test case with multiple InputData. 

[Theory]
[InlineData("Prasad", "Raveendran", "Prasad Raveendran")]
[InlineData("Prabha", "Raveendran", "Prabha Raveendran")]
public void FullName_By_MultipleInlineData(string firstName, string lastName, string expected) {
    var sut = new Employee(firstName, lastName, 12345);
    var actual = sut.FullName;
    Assert.Equal(expected, actual);
}

Execute this and see the output.

The test case has been executed successfully.

All these test cases would be successful until someone decided to change the Employee constructor.

Now I’m going to add a new parameter, BirthDay, into the Employee constructor.

My employee class become like the image below:

I added birthday into the Employee constructor. Now, check the test cases.

All three test cases have failed. There is no reason why these test cases should have broken, since we haven’t written any test case that depends on the new parameter “BirthDay”.

To resolve this, we must default to a dummy value in all these test cases.

In FullName_Return_Expected:

var sut = new Employee("Prasad", "Raveendran", 12345,25);

In FullName_By_FirstName_LastName:

var sut = new Employee(firstName, lastName, 12345,24);

In FullName_By_MultipleInlineData:

var sut = new Employee(firstName, lastName, 12345,25);

All tests have been passed.

Now we are going to see how AutoFixture will help us to improve such scenarios.

Let us create another class with a test case that will allow the Fixture class to create the Employee object.

using AutoFixture;
using AutoFixtureTutorial;
namespace AutofixtureTest {
    public class ExmployeetWithAutoFixtureTest {
        [Theory]
        [InlineData("Prasad", "Raveendran", "Prasad Raveendran")]
        [InlineData("Prabha", "Raveendran", "Prabha Raveendran")]
        public void FullName_By_MultipleInlineData(string firstName, string lastName, string expected) {
            var fixture = new Fixture();
            //AutoFixture
            var sut = fixture.Build < Employee > ().With(a => a.FirstName, firstName).With(a => a.LastName, lastName).Create();
            var actual = sut.FullName;
            Assert.Equal(expected, actual);
        }
    }
}

Go ahead and execute the test cases. All the test cases have been passed.

If we debug the test case, we will come to know how the values were assigned to the Employee Object.

Here BirthDay and EmployeeNumber have random values assigned by AutoFixture.

If we want to change the order of parameters in the Employee constructor or rearrange the existing parameter orders, these test cases will be passed, since we have implemented the AutoFixture.

Now, assume that we are going to add another property, “MiddleName,” which will be used to construct the FullName.

The modified Employee class will be looks like the below:

namespace AutoFixtureTutorial {
    public class Employee {
        public Employee(string firstName, string lastName, int employeeNumber, int birthDay, string middleName) {
            FirstName = firstName ??
                throw new ArgumentNullException(nameof(firstName));
            LastName = lastName ??
                throw new ArgumentNullException(nameof(lastName));
            EmployeeNumber = employeeNumber;
            BirthDay = birthDay;
            MiddleName = middleName ??
                throw new ArgumentNullException(nameof(middleName));
        }
        public string FirstName {
            get;
            set;
        }
        public string LastName {
            get;
            set;
        }
        public string MiddleName {
            get;
            set;
        }
        public int EmployeeNumber {
            get;
            set;
        }
        public int BirthDay {
            get;
            set;
        }
        public string FullName {
            get {
                return $ "{FirstName} {MiddleName} {LastName}";
            }
        }
    }
}

The test case will fail.

Let us debug and see what happened to the test data.

If you look at this, the actual FullName has been changed.

How this test case can be modified?

public class ExmployeetWithAutoFixtureTest {
    [Theory]
    [InlineData("Prasad", "Nair", "Raveendran", "Prasad Nair Raveendran")]
    [InlineData("Prabha", "Nair", "Raveendran", "Prabha Nair Raveendran")]
    public void FullName_By_MultipleInlineData(string firstName, string middleName, string lastName, string expected) {
        var fixture = new Fixture();
        //AutoFixture
        var sut = fixture.Build < Employee > ().With(a => a.FirstName, firstName).With(a => a.MiddleName, middleName).With(a => a.LastName, lastName).Create();
        var actual = sut.FullName;
        Assert.Equal(expected, actual);
    }
}

Now run the test and see the result.

The test case has been passed.

Whenever we are making any changes into a Class for a reason, we are forced to make changes to the corresponding test cases too.

Now let us try to do a bit of cleanup on the test cases using Auto Data Attribute.

Create a new test case with InlineAutoData Attribute.

[Theory]
[InlineAutoData("Prasad", "Nair", "Raveendran", "Prasad Nair Raveendran")]
[InlineAutoData("Prabha", "Nair", "Raveendran", "Prabha Nair Raveendran")]
public void FullName_By_AutoDataAttribute(Employee sut, string firstName, string middleName, string lastName, string expected) {
    sut.FirstName = firstName;
    sut.LastName = lastName;
    sut.MiddleName = middleName;
    var actual = sut.FullName;
    Assert.Equal(expected, actual);
}

Run this test and see the result. The test cases failed. Let us look at the error message.

[xUnit.net 00:00:01.21]   Starting:    AutofixtureTest
[xUnit.net 00:00:01.40]     AutofixtureTest.ExmployeetWithAutoFixtureTest.FullName_By_AutoDataAttribute(sut: "Prasad", firstName: "Nair", middleName: "Raveendran", lastName: "Prasad Nair Raveendran", expected: "expectedc41ae6eb-d336-4c52-ae80-c00c5172296e") [FAIL]
[xUnit.net 00:00:01.40]       System.ArgumentException : Object of type 'System.String' cannot be converted to type 'AutoFixtureTutorial.Employee'.

This happened because we are passing the string parameter first in the AutoData Attribute. The Employee object needs to be pushed all the way down. The updated test method looks like the below:

[Theory]
[InlineAutoData("Prasad", "Nair", "Raveendran", "Prasad Nair Raveendran")]
[InlineAutoData("Prabha", "Nair", "Raveendran", "Prabha Nair Raveendran")]
public void FullName_By_AutoDataAttribute(string firstName, string middleName, string lastName, string expected, Employee sut) {
    sut.FirstName = firstName;
    sut.LastName = lastName;
    sut.MiddleName = middleName;
    var actual = sut.FullName;
    Assert.Equal(expected, actual);
}

Now run the test case. It should be passed. The values that are not passing through InlineAutoData attributes will be defaulted by AutoFixture.

Now, if we are making any changes to the Employee class, this test case will run successfully. Autofixture made this test more stable, robust, and maintainable.

I hope this tutorial helps you to understand how Autofixture makes developers' lives easy when writing unit test cases.

Thank you for reading the article and leave your feedback in comment box below.