Moq.Mock <Expression <Func <T, bool >>> () - как настроить выражения в Mock с помощью Moq - PullRequest
14 голосов
/ 02 ноября 2011

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

У меня есть этот интерфейс

public interface IRepository<T> where T : class, IEntity 
{
    IQueryable<T> Find(Expression<Func<T, bool>> predicate);
    T FindIncluding(int id, params Expression<Func<T, object>>[] includeProperties);
}

И это базовая структура метода, содержащего макет, который я хотел бы настроить

public PeopleController CreatePeopleController()
{
    var mockUnitofWork = new Mock<IUnitOfWork>();
    var mockPeopleRepository = new Mock<IRepository<Person>>();

    mockPeopleRepository.Setup(r=>r.Find().Returns(new Person(){});
    mockUnitofWork.Setup(p => p.People).Returns(mockPeopleRepository.Object);
    return new PeopleController(mockUnitofWork.Object);
}

Я былпытаясь настроить Mock следующим образом:

public PeopleController CreatePeopleController()
{
    var mockUnitofWork = new Mock<IUnitOfWork>();
    var mockPeopleRepository = new Mock<IRepository<Person>>();

    mockPeopleRepository.Setup(r=>r.Find(It.isAny<Expression<Func<Person,bool>>>()).Single()).Returns(new Person(){});
    mockUnitofWork.Setup(p => p.People).Returns(mockPeopleRepository.Object);
    return new PeopleController(mockUnitofWork.Object);
}

Но система всегда выдает одно и то же исключение «System.NotSupportedException: выражение ссылается на метод, который не принадлежит к поддельному объекту ....»

Также я хотел бы добавить, что я использую MSTest и Moq

Я знаю, что настроить Mock с помощью Expression нелегко и не рекомендуется, но это очень важно для меня, потому что "Find"метод, который я часто использую в своем приложении

Ответы [ 2 ]

19 голосов
/ 02 ноября 2011

Проблема в том, что вы пытаетесь настроить метод расширения Single () как часть вашего насмешки. У вызова установки должен быть результат вашего метода, а не результат вашего метода с последующим применением к нему какого-либо метода расширения. Я бы попробовал это:

    [TestMethod]
    public void MyTestMethod()
    {
        var myMock = new Mock<IRepository<Person>>();
        myMock.Setup(r => r.Find(It.IsAny<Expression<Func<Person, bool>>>())).Returns(new List<Person>() { new Person() }.AsQueryable());

        Assert.IsTrue(true);
    }

Здесь вы просто заглушаете свой метод Find () с помощью setup и выполняете все остальные действия в предложении Returns (). Я бы предложил такой подход в целом. Программа установки должна точно отражать ваш смоделированный элемент, и вы можете выполнить кучу черной магии для вызова Returns () (или Throws () или чего угодно), чтобы заставить его делать то, что вы хотите.

(Когда я запускал этот код в VS, он прошел, поэтому он не выдавал исключение)

4 голосов
/ 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/

...