DEV Community

Chuen Lee
Chuen Lee

Posted on • Originally published at chlee.co

How to save time writing unit and integration tests with AutoFixture

Writing Unit and integration tests are generally composed of setting up relevant test data, carrying the actual execution of the test, and finally making assertions. All this can be summarised as the AAA syntax. That is, Arrange, Act, and Assert.

With AutoFixture, it can significantly save you time by taking away the tedious part of stubbing and arranging test data.

The best way to describe this is to show a traditional test example followed by how it can be replaced with AutoFixture.

A Regular Test

Let's start with a traditional test. In the example below, it demonstrates setting up a customer with fake dummy data. Then the CustomerService is used to save this customer to some data store. This may be a contrived example, but it demonstrates how much time it can take to stub fake data such as this.

Also, note that a Customer in this example has an Address which is another entity that needs to be stubbed.

Libraries do exist to assist in generating fake data such as Bogus. But still one has to write a few lines of code.

public class CustomerTests
{
    [Fact]
    public void AddCustomer_When_valid_customer_should_save_successfully()
    {
        // arrange
        var customer = new Customer
        {
            FirstName = "Paula",
            LastName = "McKinney",
            EmailAddress = "paula_mckinney@fakeemail.org",
            DateOfBirth = DateTime.UtcNow.AddYears(-30),
            Address =
                new Address {
                    AddressLine1 = "4989 Wayside Lane",
                    AddressLine2 = "Hayward",
                    ZipOrPostcode = "94541",
                    Country = "United States"
                }
        };

        // act
        var customerService = new CustomerService();
        customerService.AddCustomer(customer);

        // assert
        // make assertions here.

    }
}
Enter fullscreen mode Exit fullscreen mode

Using Autofixture

Let's take the example above and replace it with AutoFixture. To start using AutoFixture, simply add the nuget package

via package manager in Visual Studio

Install-Package AutoFixture
Enter fullscreen mode Exit fullscreen mode

via dotnet CLI

dotnet add package AutoFixture
Enter fullscreen mode Exit fullscreen mode
 [Fact]
public void AddCustomer_When_valid_customer_should_save_successfully_with_autofixture()
{
    // arrange
    var fixture = new Fixture();
    var customer = fixture.Create<Customer>();

    // act
    var customerService = new CustomerService();
    customerService.AddCustomer(customer);

    // assert
    // make assertions here.
}
Enter fullscreen mode Exit fullscreen mode

Hopefully, you can see that it just takes two lines of code from AutoFixture. Wait. What!? For real? Yes, for real. Just two lines of code. Here are the two lines again:

var fixture = new Fixture();
var customer = fixture.Create<Customer>();
Enter fullscreen mode Exit fullscreen mode

AutoFixture does all the work and just to prove it, here's a screenshot of debugging the test and inspecting the customer object.

Alt Text

You will see that AutoFixture has pre-populated all the fields with dummy data. It has also taken the initiative of stubbing the Address entity as well.

Autofixture Customisations

What if we want to control some of the behaviour of AutoFixture? Take, for example, the customer's date of birth. What if we wanted to set a specific date of birth for the customer as I may want to test some business logic around the date of birth?

To achieve this, you can use the With method to override a particular property.

var fixture = new Fixture();
var customer = fixture.Build<Customer>()
    .With(c => c.DateOfBirth, DateTime.UtcNow.AddYears(-30))
    .Create();
Enter fullscreen mode Exit fullscreen mode

This will set the DateOfBirth to 10 years in the past.

You can also ignore or disable some properties that you do not want AutoFixture to populate. For instance, the example below will ignore the Address property.

var customer = fixture.Build<Customer>()
                .Without(c => c.Address)
                .Create();
Enter fullscreen mode Exit fullscreen mode

Using AutoData

You can take it one step further and use the AutoData attribute.

Be sure to add the nuget package.

via package manager in Visual Studio

Install-Package AutoFixture.Xunit2
Enter fullscreen mode Exit fullscreen mode

via dotnet CLI

dotnet add package AutoFixture.Xunit2
Enter fullscreen mode Exit fullscreen mode

XUnit example

 [Theory, AutoData]
public void AddCustomer_When_valid_customer_should_save_successfully_with_autofixture(Customer customer)
{
    // act
    var customerService = new CustomerService();
    customerService.AddCustomer(customer);

    // assert
    // make assertions here.
}
Enter fullscreen mode Exit fullscreen mode

NUnit example

 [Test, AutoData]
public void AddCustomer_When_valid_customer_should_save_successfully_with_autofixture(Customer customer)
{
    // act
    var customerService = new CustomerService();
    customerService.AddCustomer(customer);

    // assert
    // make assertions here.

}
Enter fullscreen mode Exit fullscreen mode

As you can see from both XUnit and NUnit examples above, the Customer is passed as a parameter in the method. There was no need to use the var fixture = new Fixture(); as you have seen from the earlier examples.

This way of testing can be very useful, as well as saving considerable amount of time coding stub and fake data.

Summary

As you can see, with the introduction of AutoFixture, it can significantly reduce writing test data for the arrangement part of tests and also reduce the amount of code. With less code, less maintenance.

For further examples, checkout AutoFixture's cheat sheet and also the AutoFixture GitHub page.

Top comments (1)

Collapse
 
bchavez profile image
Brian Chavez

You can also use AutoBogus and Bogus together to make a 1-liner. No manual rules necessary:

void Main()
{
   var c = AutoBogus.AutoFaker.Generate<Customer>();
   c.Dump();
}

public class Customer
{
   public string FirstName { get; set; }
   public string LastName { get; set; }
   public string EmailAddress {get;set;}
   public DateTime DateOfBirth {get;set;}
   public Address Address {get;set;}
};

public class Address
{
   public string AddressLine1 { get; set; }
   public string AddressLine2 { get; set; }
   public string ZipOrPostcode { get; set; }
   public string Country {get;set;}
}
{
  "FirstName": "Mauritania",
  "LastName": "Path",
  "EmailAddress": "Open-source",
  "DateOfBirth": "2019-09-11T10:34:41.9215413-07:00",
  "Address": {
    "AddressLine1": "Auto Loan Account",
    "AddressLine2": "Illinois",
    "ZipOrPostcode": "Azerbaijanian Manat",
    "Country": "Liaison"
  }
}