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

2

4

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.

Angulate answered 18/10, 2012 at 20:16 Comment(0)
C
5

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
Capsular answered 19/10, 2012 at 0:51 Comment(3)
I assumed it would determine what to return based on what I setup in the Customization, as in, the Mock knows I'm calling a method with a return of IQueryable<Foo>, so it'll use the values I registered for that. As for the Customization, this is a very simplified version of what my actual test class looks like.Angulate
Is this still the case? Autofixture seems to be able to handle generics just fine. fixture.Create<Task<MyContainer<List<string>>>() is able to properly generate all the expected objects all the way down to the string level. But if I have an interface than mockedService.Object.Get<string>() returns a null and can't seem to figure out that Get<string>(), which has a signature of T Get<T>() should return a string. I'm having a hard time understanding why it should work in the first instance but not when using AutoMoq.Abercromby
@Abercromby The answer you got here seems correct.Capsular
D
0

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"));
    }
}
Drumlin answered 18/2, 2020 at 16:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.