Creating tests with anonymous variables using AutoFixture

Unit tests are the initial barrier to verify whether a unit functions as it was designed within a software system. Often, when testing such a component, the first idea is to create predefined data and use it within the test functions. However, this practice can lead the programmer to believe that the test – if it passes – covers the most trivial cases for the problem domain. Considering that the data flow in a real system is not static, using them in tests as input for functions can pose some issues, such as a lack of reliability in the system to perform the expected action and potential bugs that may go unnoticed during testing.

In this regard, some libraries emerge to tackle the issue of static data. One of them, used within the .NET environment is AutoFixture. It is an open-source library with a focus on developing unit tests following the Test-Driven Development (TDD) methodology. Its primary mantra is that we, as developers, should create tests not based on the code that implements the algorithm but on the algorithm’s signature. In other words, test functions should not receive static data but dynamic data with the intention of working in any case – and that’s precisely what AutoFixture does. It provides a range of operations and mechanisms that we can use in our tests to generate dynamic data.

In this post, we will discuss how to use AutoFixture to create self-generated data structures.

Preparing the environment

To run the code in this post, you’ll need to have AutoFixture and other testing dependencies installed1 in a Visual Studio project.

Unraveling the class to be tested

In this post we will test existing classes. This has a positive aspect because refactoring existing code is one of the developer’s tasks. In this context, we will test the Dolar class – mentioned in another post on this blog – which contains the following implementation:

public class Dolar
{
    public int Amount { get; private set; }

    public Dolar(int amount)
    {
        Amount = amount;
    }

    public void Times(int multiplier)
    {
        Amount *= multiplier;
    }
}

The method we want to test is the Times method. One of the tests we can perform is to pass a positive integer that will multiply the Amount property. This way, we will verify if the operation is performed correctly and if the Amount value contained in an object of the Dolar class is in accordance with this operation.

Creating tests

We can create the DolarTests class with the following test:

public class DolarTests
{
    private readonly IFixture fixture = new Fixture();

    [Fact]
    public void Times_WithPositiveMultiplier_ShouldMultiplyCorrectly()
    {
        var initialValue = fixture.Create<int>(); //1
        var dolar = new Dolar(initialValue);
        var multiplier = fixture.CreateInt(minValue: 1); //2

        dolar.Times(multiplier);
        var result = dolar.Amount;

        var expectedResult = initialValue * multiplier;
        result.Should().Be(expectedResult);
    }
}

The standard call to create an object with automatically generated variables using AutoFixture is fixture.Create<type of object>(). In this test, we use this method at (1) to generate any integer. Furthermore, at (2), we use an extension method2 to create a positive integer3. At the end of the test, we compare the result of the tested method with the manually performed multiplication.

Running the test, we can verify that it passed without any issues.

Okay, that was easy, but we can simplify the test. Instead of manually setting the Amount of a Dolar object, we can let the fixture create it. To achieve this, we can use the fixture to create the object and then retrieve the Amount from the created object.

var dolar = fixture.Create<Dolar>();
var initialValue = dolar.Amount;

Now the instantiation and creation of all the properties and instance variables of public nature of the Dolar class are handled by AutoFixture. With it, we have significant power in our hands: classes can be created automatically without us worrying about their initialization.

Classes with circular references

Classes with circular references are classes that have a specific type of relationship: A has B, and B has A. There are certain scenarios where implementations of such relationships are unavoidable. One of them is the following class that simulates a Doubly Linked List4:

public class Node
{
    public Node? Next { get; set; }

    public Node? Previous { get; set; }

    ...
}

In this class, we can immediately see that there is a circular reference – Node has a property Next that is of type Node. In theory, the fixture would keep creating classes infinitely until a memory shortage error occurs.

We can create the following test to verify our theory:

[Fact]
public void GetLastNode_WithManyNodes_ShouldReturnLast()
{
    var nodes = fixture.CreateMany<Node>();
}

This test uses the CreateMany<Node> method, which returns an IEnumerable of the Node type, meaning multiple objects of the Node type.

When trying to run the test, we receive the following error:

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

[xUnit.net 00:00:00.22]       Request path:
[xUnit.net 00:00:00.22]         Peteca.Node Next
[xUnit.net 00:00:00.22]           Peteca.Node

We have noticed that the library contains mechanisms to prevent objects from being created infinitely. However, we are still facing the problem of not being able to create the class with AutoFixture.

Using Build for object configuration

The Build method allows us to customize the creation of an object. It returns an ICustomizationComposer, which contains some methods for customizing the object we are creating. In our case, we will use the Without method to exclude the property indicated by the lambda expression5 from automatic creation. We use it as follows:

var nodes = fixture
    .Build<Node>()
    .Without(n => n.Next)
    .Without(n => n.Previous)
    .CreateMany();

In this code, we use Without to exclude the Next and Previous properties from being created by the fixture.

Having seen the use of Build, we can create the following test:

[Fact]
public void GetLastNode_WithManyNodes_ShouldReturnLast()
{
    var nodes = fixture
        .Build<Node>()
        .Without(n => n.Next)
        .Without(n => n.Previous)
        .CreateMany()
        .ToList();

    var initialNode = fixture
        .Build<Node>()
        .Without(n => n.Next)
        .Without(n => n.Previous)
        .Create();

    var expectedLastNode = nodes.Last();
    initialNode.AppendNodes(nodes);

    var lastNode = initialNode.GetLastNode();
    lastNode.Should().Be(expectedLastNode);
}

In this test we used Build to create multiple objects of the Node type and added these objects to an initial node through AppendNodes. In the end, we tested whether the GetLastNode method is indeed returning the final node.

Conclusion

In this post, we have seen how to use the AutoFixture library to create individual objects and multiple objects of the same type. We were also able to customize the creation of these objects to remove circular references or even properties that don’t make sense for testing. With these tools, tests can be conducted more safely, allowing for a broader range of scenarios to be tested.

  1. Install and manage packages in Visual Studio using the NuGet Package Manager | Microsoft Learn ↩︎
  2. Extension Methods – C# Programming Guide – C# | Microsoft Learn ↩︎
  3. c# – AutoFixture for number ranges – Stack Overflow ↩︎
  4. Doubly Linked list – Wikipedia ↩︎
  5. Lambda expressions – Lambda expressions and anonymous functions – C# | Microsoft Learn ↩︎

Posted

in

Luis Felipe Avatar

by

Comments

Leave a Reply

%d bloggers like this: