Классы насмешки, которые реализуют IQueryable с Moq - PullRequest
37 голосов
/ 13 августа 2011

Я провел вечер, пытаясь смоделировать объект, который реализует IQueryable:

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

Лучшее, что я мог придумать, это что-то вроде этого:

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);

Есть ли ещекраткий способ сделать то же самое?Было бы проще выставить свойство / метод в IRepo, который возвращает IQueryable и просто издеваться так:

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

Но это не то, что я хочу сделать =)

Ответы [ 5 ]

44 голосов
/ 16 декабря 2011

В этом нет ничего нового, просто более чистый способ сделать это. У меня также есть репозитории, где сам репозиторий также является IQueryable, поэтому мне нужно было то же самое. Я просто поместил ваш код в метод расширения, подобный этому, на корневом уровне моего тестового проекта, чтобы сделать его доступным для всех тестов:

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);
    }
}

В основном это просто возможность многократного использования, так как вы, вероятно, захотите сделать это в нескольких тестах, и в каждом тесте это проясняет намерение, а беспорядок минимален. :)

8 голосов
/ 16 апреля 2014

Ответ Руны потрясающий и сэкономил мне время на то, чтобы понять, как сделать то же самое. Небольшая ошибка в том, что если вы вызываете некоторые методы расширения IQueryable на вашем IQueryable дважды (например, ToList ()), то во второй раз вы не получите никаких результатов. Это потому, что перечислитель находится в конце и нуждается в сбросе. Используя Rhinomocks, я изменил реализацию для GetEnumerator на:

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

Надеюсь, это сэкономит кому-то время.

6 голосов
/ 17 марта 2014

Мне нравится ответ Руны.Вот общая версия IQueryable:

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);
}
2 голосов
/ 13 августа 2011

Я думаю, это лучшее, что вы можете сделать с Moq. Я думаю, что более читабельным вариантом будет бросить свой собственный FakeRepo<T>, который получен из System.Linq.EnumerableQuery<T>:

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

Обновление: Возможно, вам удастся сделать это, высмеивая EnumerableQuery<T>, затем используя As<T>():

var items = new Item[0];

var repo = new Mock<EnumerableQuery<Item>(items).As<IRepo>();
0 голосов
/ 11 февраля 2015

У меня была такая же проблема. Я исправил это, изменив эту строку

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

до

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

Я надеюсь, что дополнительные комментарии здесь не требуются.

...