Reputation: 918
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.
Upvotes: 4
Views: 7147
Reputation: 18556
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"));
}
}
Upvotes: 0
Reputation: 233377
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
Upvotes: 5