Search code examples
c#testingautofixturenunit-3.0

AutoFixture - Creation of "Valid" and "Invalid" instances and [AutoData]


I created following sample model:

internal sealed class Bike : IVehicle
{
    public Bike(
        Engine engineType,
        WindowHue windowHue,
        Vehicle transport,
        ushort wheelsCount,
        string name) =>
        (EngineType, WindowHue, Transport, WheelsCount, Name) =
        (engineType, windowHue, transport, wheelsCount, name);

    public WindowHue WindowHue { get; }
    public Engine EngineType { get; }
    public Vehicle Transport { get; }
    public ushort WheelsCount { get; }
    public string Name { get; }
}

I'm currently writing unit tests for Bike validator and I would like to use AutoFixture to create instances of Bike class that have values that are considered valid and invalid. Is there a way to instruct AutoFixture how to create those types of instances and tell it to fetch valid or invalid one depending on unit test that is running? For example: in test case that checks whether valid instance of Bike class passes validation I would like AutoFixture to create a valid Bike instance. I attempted to achieve this behavior by creation of custom specimen builders but it seems that the last one that is registered is used to create an actual instance of requested type. Other idea was to create builder class that would use AutoFixture to create valid and invalid instances [via "Create" method] and use it in test cases, but I think that is not a good idea, since it leads to creation of redundant code [builder class per tested model]. If above behavior is possible, then is there a way to create such instances by using [AutoData] attribute, so that I don't have to call AutoFixture in test case body?


Solution

  • Yes you can, however the complexity of your setup code will depend on the complexity of your domain.

    You could declare a customization that would build your DTO models with valid data, then use it via a custom [AutoData] attribute, and inside the test customize some of the DTOs with invalid data using .Customize<T>() or .Build<T>().

    Now if you want to provide your invalid DTOs from the test parameters you could try to implement a [Invalid] attribute, that would customize individual test parameters, then use [Frozen] to use the value in other generated models.

    For the [Invalid] attribute you can implement either the CustomizeAttribute from the AutoFixture.NUnit3 package or the IParameterCustomizationSource from AutoFixture.

    As you'll see the output of the customization attribute is an ICustomization meaning inside the attribute you'll likely have a dictionary that outputs a customization for an invalid entity depending on the parameter type.

    NB: I would really advise you use the first approach since it makes it obvious in what way the input data is invalid and makes asserting the results easier.