How do I get AutoFixture AutoMoq to return results from injected services in an instantiated object?

StackOverflow https://stackoverflow.com/questions/12963019

  •  08-07-2021
  •  | 
  •  

Question

I am trying to test a service class that consumers a repository service. I have customizations set up that I believe should work with my repository service, but instead return default Anonymous results. If you look at the code sample below, I'm trying to get the "Foo" objects that I registered in the Customization Class back when I call the svc.GetFoos method, instead I get nothing:

void Main()
{
    var fixture = new Fixture().Customize(
        new CompositeCustomization(
            new Customization(),
            new AutoMoqCustomization())); 

    var svc = fixture.CreateAnonymous<Bar>(); 

    Console.Write(svc.GetFoos().Count()); 
}

// Define other methods and classes here
public class Bar
{

    public IQueryable<Foo> GetFoos()
    {
        return _rep.Query<Foo>(); 
    }

    public Bar(IRepository rep) { _rep = rep;  }

    private IRepository _rep; 
}

public class Foo
{
    public string Name {get;set;}   
}

public class Customization : ICustomization
{
    public void Customize(IFixture fixture)
    {
        var f = fixture
                .Build<Foo>()
                .With(x => x.Name, "FromCustomize")
                .CreateMany(2)
                .AsQueryable();
        fixture.Register<IQueryable<Foo>>(() => f); 
    }
}

public interface IRepository
{
    IQueryable<T> Query<T>(); 
}

If I add the following code to the Main method after the fixture instantiation, it works how I want, but then I'm manually setting up my mocks, and I'm not sure what AutoFixture AutoMoq is getting me:

var mock = fixture.Freeze<Mock<IRepository>>(); 
mock
    .Setup(x => x.Query<Foo>())
    .Returns(fixture.CreateAnonymous<IQueryable<Foo>>); 

Thanks.

Was it helpful?

Solution

AutoFixture.AutoMoq works as an Auto-Mocking Container. It'll automatically compose object graphs by injecting Mock<T> instances into any consumer of said T.

It can't configure the Mock<T> instances for you - after all, how could it? Only you (the test writer) knows what the appropriate interaction should be.

So the code you present, including the calls to Setup and Returns, is correct, although you may consider whether or not the Customization class is overkill.

If you need to automate a lot of repetitious setup of Moq, you should consider

  • whether the interface design and the consumption pattern is appropriate
  • if a Fake wouldn't be a better option than a dynamic mock

OTHER TIPS

I recently came across a similar topic. I was unable to execute queries for my automatically created IQueryable<T>.

Expression of type 'System.Object' cannot be used for parameter of type 'System.Linq.IQueryable`1[System.String]' of method 'Boolean Any...

I worked around this by creating a special specimen builder which always creates an array instead, and returns it AsQueryable. In the end I did not use the code, but it works.

Very rough obviously, just trying things out...

void Main()
{
    Fixture fixture = new Fixture();

    fixture.Customize(new AutoMoqCustomization { ConfigureMembers = true, Relay = new QueryableBuilder() });

    // Possible ways to "override" the AutoMoqBehavior
//  fixture.Inject<string>("hallO");
//  fixture.Inject(new[] { "asd" });
//  fixture.Inject(new[] { "asds" }.AsQueryable());

    var queryable = fixture.Create<Foo>();
    queryable.Bar();

    fixture.Create<IQueryable<string>>().Any(x => x.Equals("asd"));
}

public class QueryableBuilder : ISpecimenBuilder
{
    MockRelay _Base = new MockRelay();

    public object Create(object request, ISpecimenContext context)
    {
        var t = request as Type;
        if (t == null ||
            !t.IsGenericType ||
            t.GetGenericTypeDefinition() != typeof(IQueryable<>))
            return _Base.Create(request, context);

        var queryableTypeName = typeof(IQueryable<>).Name;
        if (t.Name != queryableTypeName)
            return _Base.Create(request, context);

        var entityType = t.GetGenericArguments().Single();

        var tt = entityType.MakeArrayType();
        dynamic blbb = context.Resolve(tt);
        return ((IEnumerable)blbb).AsQueryable();
    }
}

public interface IHaveQueryable {
    IQueryable<string> Queryable {get;}
}

public class Foo {

    readonly IHaveQueryable _Queryable;

    public Foo(IHaveQueryable queryable){
        _Queryable = queryable;
    }

    public bool Bar(){
        return _Queryable.Queryable.Any(x => x.Equals("bar"));
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top