Search code examples
c#xunitautofixtureautomoq

AutoFixture AutoMoq partially covers class constructor


I am using AutoFixture AutoMoq and it's greatly simplifies the mock for all interface used in class and initialize it.

Though I noticed the constructor code is partially covered, can we modify AutoMoqDataAttribute so that constructor ArgumentNullException can also be covered?

enter image description here

public class ServiceTest
{
    [Theory, AutoMoqData]
    public async Task Do_Test_For_DoMethod(Service sut)
    {
        await sut.DoMethod();
    }
}

AutoMoqData attribute class,

public class AutoMoqDataAttribute : AutoDataAttribute
{
    public AutoMoqDataAttribute()
        : base(() => new Fixture().Customize(new AutoMoqCustomization()))
    {

    }
}

Service class code,

 public class Service
{
    private readonly IServiceClient _serviceClient;
    private readonly ILogger<Service> _logger;

    public Service(IServiceClient serviceClient, ILogger<Service> logger)
    {
        _serviceClient = serviceClient ?? throw new ArgumentNullException(nameof(serviceClient));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }


    public async Task DoMethod()
    {

        await _serviceClient.Do();
    }
}

Solution

  • It is not the responsibility of the AutoDataAttribute to know how to cover your code. It is the developers responsibility to express intent and explicitly cover the scenario.

    In your case the uncovered branches are the null guards. Luckily AutoFixture provides a library just for that, AutoFixture.Idioms.

    This library provides idiomatic assertions, that encapsulate most basic test scenarios like null guards, property setters, equality members and so on.

    Using this library you can write a test like the following.

    [Theory, AutoMockData]
    void CheckGuards(GuardClauseAssertion assertion)
    {
       assertion.Verify(typeof(Service));
    }
    

    This should cover all the parameters of all the constructors, and the nice part is that it uses your customization, so it will generate valid data when attempting to instantiate your object.