46

I spent an evening trying to mock an object that implements IQueryable:

public interface IRepo<T> : IQueryable<T>
{
}

The best I could come up with is something like this:

var items = new Item[] {}.AsQueryable();

var repo = new Mock<IRepo>();
repo.Setup(r => r.GetEnumerator()).Returns(items.GetEnumerator());
repo.Setup(r => r.Provider).Returns(items.Provider);
repo.Setup(r => r.ElementType).Returns(items.ElementType);
repo.Setup(r => r.Expression).Returns(items.Expression);

Is there a more concise way to do the same? It would be easier to expose a property/method in IRepo that returns IQueryable and the simply mock like this:

repo.Setup(r => r.GetItems()).Returns(new Items[]{ }.AsQueryable());

But this is not what I want to do =)

LeffeBrune
  • 3,441
  • 1
  • 23
  • 36

5 Answers5

56

This is nothing new, just a cleaner way of doing it. I also have repositories where the repository itself is also an IQueryable, so I needed the same thing. I basically just put your code into an extension method like this at the root level of my test project, to make it available to all tests:

public static class MockExtensions
{
    public static void SetupIQueryable<T>(this Mock<T> mock, IQueryable queryable)
        where T: class, IQueryable
    {
        mock.Setup(r => r.GetEnumerator()).Returns(queryable.GetEnumerator());
        mock.Setup(r => r.Provider).Returns(queryable.Provider);
        mock.Setup(r => r.ElementType).Returns(queryable.ElementType);
        mock.Setup(r => r.Expression).Returns(queryable.Expression);
    }
}

This basically just offers reusability, since you're likely to want to do this in several tests, and in each test it makes the intention clear and the mess minimal. :)

Alex KeySmith
  • 16,657
  • 11
  • 74
  • 152
Rune Jacobsen
  • 9,907
  • 11
  • 58
  • 75
  • Great idea. We've implemented the same, but have also included Rob's enumerator reset for cases where we query the repo collection multiple times. – MCattle May 13 '14 at 15:45
8

Rune's answer is awesome and saved me time figuring out how to do the same. Small gotcha is that if you call some IQueryable extension methods on your IQueryable twice (e.g. ToList()) then the second time you'll get no results back. That's because the enumerator is at the end and needs resetting. Using Rhinomocks I changed the implementation for GetEnumerator to:

mock.Stub(r => r.GetEnumerator()).Do((Func<IEnumerator<T>>) (() => { 
    var enumerator = queryable.GetEnumerator();
    enumerator.Reset();
    return enumerator;
}));

Hope that saves someone some time.

Rob
  • 663
  • 8
  • 8
  • Nice one Rob, thanks. I'll look at achieving the equivalent for Moq as I'm experiencing this issue. – The Senator Apr 16 '14 at 14:17
  • 1
    @the-senator please post Moq solution – OzBob Apr 28 '15 at 02:36
  • @OzBob after I returned to this following a Moq update my original solution just worked without the need to reset the enumerator. Can you confirm that you are still witnessing this annoyance with a modern Moq version? If so I'll track down my unit test that proved it and have another go! – The Senator May 02 '15 at 15:27
  • Thank you @Rob so much! Lost have a day with this issue until I found your post. – Delorian May 28 '15 at 04:59
  • 2
    This saved my bacon for Moq as well! `mockSet.As>().Setup(m => m.GetEnumerator()).Returns(() => { var enumerator = queryable.GetEnumerator(); enumerator.Reset(); return enumerator; });` – Larsbj Jan 22 '16 at 12:12
6

I like Rune's answer. Here's a generic IQueryable version:

public static void SetupIQueryable<TRepository, TEntity>(this Mock<TRepository> mock, IQueryable<TEntity> queryable)
   where TRepository : class, IQueryable<TEntity>
{
    mock.Setup(r => r.GetEnumerator()).Returns(queryable.GetEnumerator());
    mock.Setup(r => r.Provider).Returns(queryable.Provider);
    mock.Setup(r => r.ElementType).Returns(queryable.ElementType);
    mock.Setup(r => r.Expression).Returns(queryable.Expression);
}
Mark Good
  • 4,271
  • 2
  • 31
  • 43
3

I think that's about the best you can do with Moq. I think a more readable option would be to roll your own FakeRepo<T> that derives from System.Linq.EnumerableQuery<T>:

public class FakeRepo<T> : EnumerableQuery<T>, IRepo<T>
{
    public FakeRepo(IEnumerable<T> items) : base(items) { }
}

Update: You might be able to pull this off by mocking EnumerableQuery<T> then using As<T>():

var items = new Item[0];

var repo = new Mock<EnumerableQuery<Item>(items).As<IRepo>();
dahlbyk
  • 75,175
  • 8
  • 100
  • 122
0

I had the same issue. I've fixed it by changing this line

mock.Setup(r => r.GetEnumerator()).Returns(queryable.GetEnumerator());

to

mock.Setup(r => r.GetEnumerator()).Returns(queryable.GetEnumerator);

I hope that additional comments are not required here.

akekir
  • 523
  • 4
  • 9