Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AutoFixture auto setup return type of Task<IEnumerable<>> customization

I'm using AutoFixture with AutoMoqCustomization in my tests.

I have a service which is a dependency of the system under test:

ISomeService
{
    Task<IEnumerable<int>> Get();
}

I call it inside the system under test:

var collection = await _someService.Get(); // collection is empty

I don't care what's inside the collection, but I need the collection not to be empty. I do it this way:

_fixture.Freeze<Mock<ISomeService>>()
            .Setup(service => service.Get())
            .Returns(Task.FromResult(_fixture.CreateMany<int>()));

It looks like it should be done with a customization. I created and registered one:

public class TaskCollectionCustomization : ICustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Customizations.Add(
            new FilteringSpecimenBuilder(
                new TaskCollectionBuilder(),
                new GenericTypeSpecification(typeof(Task<>))));
    }

    private class TaskCollectionBuilder : ISpecimenBuilder
    {
        public object Create(object request, ISpecimenContext context)
        {
            // never enters here
        }
    }
}

The problem is it's Create method is never entered. Any ideas or ready-to-serve solutions?

EDIT

Adding GenericTypeSpecification source

public class GenericTypeSpecification : IRequestSpecification
{
    private readonly Type _type;

    public GenericTypeSpecification(Type type)
    {
        _type = type;
    }

    public bool IsSatisfiedBy(object request)
    {
        var requestedType = request as Type;

        return requestedType != null &&
               requestedType.IsGenericType &&
               requestedType.GetGenericTypeDefinition() == _type;
    }
}
like image 747
Andrzej Gis Avatar asked Nov 24 '25 18:11

Andrzej Gis


1 Answers

AutoFixture already supports Tasks out of the box, as evidenced by this Characterization Test:

[Fact]
public void AutoFixtureAlreadySupportsTasks()
{
    var fixture = new Fixture();
    var t = fixture.Create<Task<IEnumerable<int>>>();
    Assert.NotEmpty(t.Result);
}

Thus, all you need to configure a Test Double of your Service is something like this:

[Fact]
public void ConfigureMock()
{
    var fixture = new Fixture().Customize(new AutoMoqCustomization());
    fixture.Freeze<Mock<ISomeService>>()
        .Setup(s => s.Get())
        .Returns(fixture.Create<Task<IEnumerable<int>>>());

    var svc = fixture.Create<ISomeService>();

    Assert.NotEmpty(svc.Get().Result);
}

If you think that's too much work, you can also ask AutoConfiguredMoqCustomization to do that for you instead, like this:

[Fact]
public void SimplestCustomization()
{
    var fixture = 
        new Fixture().Customize(new AutoConfiguredMoqCustomization());
    var svc = fixture.Create<ISomeService>();
    Assert.NotEmpty(svc.Get().Result);
}

However, personally, I'm not a big fan of auto-configured Test Doubles, as I believe that explicit is better than implicit, and Test Double configuration ought to be an explicit part of a unit test, because it describes the Indirect Input for the test case.

like image 103
Mark Seemann Avatar answered Nov 27 '25 07:11

Mark Seemann



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!