Moq.Mock <T>- как настроить метод, который принимает выражение - PullRequest
28 голосов
/ 02 мая 2010

Я проверяю интерфейс моего репозитория и не уверен, как настроить метод, который принимает выражение и возвращает объект? Я использую Moq и NUnit

Интерфейс:

public interface IReadOnlyRepository : IDisposable
{
    IQueryable<T> All<T>() where T : class;
    T Single<T>(Expression<Func<T, bool>> expression) where T : class;
}

Тест с уже установленной IQueryable, но не знаю, как настроить T Single:

private Moq.Mock<IReadOnlyRepository> _mockRepos;
private AdminController _controller;
[SetUp]
public void SetUp()
{
    var allPages = new List<Page>();
    for (var i = 0; i < 10; i++)
    {
        allPages.Add(new Page { Id = i, Title = "Page Title " + i, Slug = "Page-Title-" + i, Content = "Page " + i + " on page content." });
    }
    _mockRepos = new Moq.Mock<IReadOnlyRepository>();
    _mockRepos.Setup(x => x.All<Page>()).Returns(allPages.AsQueryable());
    //Not sure what to do here???
    _mockRepos.Setup(x => x.Single<Page>()
    //----
    _controller = new AdminController(_mockRepos.Object);
}

Ответы [ 4 ]

40 голосов
/ 02 мая 2010

Вы можете настроить его так:

_mockRepos.Setup(x => x.Single<Page>(It.IsAny<Expression<Func<Page, bool>>>()))//.Returns etc...;

Однако вы сталкиваетесь с одним из недостатков Moq. Вы хотели бы поместить здесь фактическое выражение вместо использования It.IsAny, но Moq не поддерживает настройку методов, которые принимают выражения с конкретными выражениями (это сложная функция для реализации). Трудность возникает из-за необходимости выяснить, эквивалентны ли два выражения.

Таким образом, в вашем тесте вы можете передать любой Expression<Func<Page,bool>>, и он передаст обратно все, что вы настроили для возврата макета. Значение теста немного разбавлено.

7 голосов
/ 27 июня 2012

Пусть вызов .Returns возвращает результат выражения для вашей переменной allPages.

_mockRepos.Setup(x => x.Single<Page>(It.IsAny<Expression<Func<Page, bool>>>()))
    .Returns( (Expression<Func<Page, bool>> predicate) => allPages.Where(predicate) );
4 голосов
/ 01 апреля 2014

Я обнаружил, что It.Is<T> следует использовать вместо It.IsAny<T> для более точных результатов.

Page expectedPage = new Page {Id = 12, Title = "Some Title"};
_mockRepos.Setup(x => x.Single<Page>(It.Is<Expression<Func<Page, bool>>>(u => u.Compile().Invoke(expectedPage))))
                       .Returns(() => expectedPage);
1 голос
/ 25 апреля 2013

Использование Moq's It.IsAny<> без .CallBack заставляет вас писать код, который не охватывается вашим тестом.Вместо этого он позволяет любому запросу / выражению вообще проходить, что делает ваш макет практически бесполезным с точки зрения модульного тестирования.

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

mockPeopleRepository
  .Setup(x => x.Find(ThatHas.AnExpressionFor<Person>()
    .ThatMatches(correctPerson)
    .And().ThatDoesNotMatch(deletedPerson)
    .Build()))
  .Returns(_expectedListOfPeople); 

Вот статья в блоге, в которой говорится об этом и дается исходный код: http://awkwardcoder.com/2013/04/24/constraining-mocks-with-expression-arguments/

...