Проверка вызова метода с лямбда-выражением - Moq - PullRequest
15 голосов
/ 11 июля 2011

У меня есть реализация Unit of Work со следующим методом:

T Single<T>(Expression<Func<T, bool>> expression) where T : class, new();

, и я называю его, например, так:

var person = _uow.Single<Person>(p => p.FirstName == "Sergi");

Как можноЯ проверяю, что метод Single был вызван с аргументом FirstName == "Sergi"?

Я пробовал следующее, но безрезультатно:

// direct approach 
session.Verify(x => x.Single<Person>(p => p.FirstName == "Sergi"));

// comparing expressions
Expression<Func<Person, bool>> expression = p => p.FirstName == "Sergi");

session.Verify(x => x
    .Single(It.Is<Expression<Func<Person, bool>>>(e => e == expression));

Все они приводят кследующая ошибка:

Ожидаемый вызов на макет хотя бы один раз, но так и не был выполнен

Есть идеи о том, как это можно сделать?Я использую последнюю версию Moq от NuGet, версия 4.0.10827.0

ОБНОВЛЕНИЕ: конкретный пример

Я вижу, чтовсякий раз, когда я использую строковые литералы внутри лямбды, Verify работает.Как только я сравниваю переменные, происходит сбой.Пример:

// the verify
someService.GetFromType(QuestionnaireType.Objective)

session.Verify(x => x.Single<Questionnaire>(q => 
    q.Type == QuestionnaireType.Objective));


// QuestionnaireType.Objective is just a constant:
const string Objective = "objective";


// the method where it's called (FAILS):
public Questionnaire GetFromType(string type)
{
    // this will fail the Verify
    var questionnaire = _session
        .Single<Questionnaire>(q => q.Type == type);
}

// the method where it's called (PASSES):
public Questionnaire GetFromType(string type)
{
    // this will pass the Verify
    var questionnaire = _session
        .Single<Questionnaire>(q => q.Type == QuestionnaireType.Objective);
}

Почему Verify терпит неудачу, как только я использую параметр метода в лямбда-выражении?

Как правильно написать этот тест?

Ответы [ 2 ]

11 голосов
/ 11 июля 2011

Прямой подход у меня прекрасно работает:

// direct approach 
session.Verify(x => x.Single<Person>(p => p.FirstName == "Sergi"));

Объект выражения не возвращает true для эквивалентных выражений, поэтому произойдет сбой:

// comparing expressions
Expression<Func<Person, bool>> expression = p => p.FirstName == "Sergi");

session.Verify(x => x
    .Single(It.Is<Expression<Func<Person, bool>>>(e => e == expression));

Чтобы понять почему, запустите следующий тест NUnit:

[Test]
public void OperatorEqualEqualVerification()
{
    Expression<Func<Person, bool>> expr1 = p => p.FirstName == "Sergi";
    Expression<Func<Person, bool>> expr2 = p => p.FirstName == "Sergi";
    Assert.IsTrue(expr1.ToString() == expr2.ToString());
    Assert.IsFalse(expr1.Equals(expr2));
    Assert.IsFalse(expr1 == expr2);
    Assert.IsFalse(expr1.Body == expr2.Body);
    Assert.IsFalse(expr1.Body.Equals(expr2.Body));
}

И, как показывает приведенный выше тест, сравнение по телу выражения также не удастся, но сравнение строк работает, так что это также работает:

// even their string representations!
session.Verify(x => x
    .Single(It.Is<Expression<Func<Person, bool>>>(e => 
        e.ToString() == expression.ToString()));

А вот еще один стиль тестирования, который вы можете добавить к арсеналу, который также работает:

[Test]
public void CallbackVerification()
{
    Expression<Func<Person, bool>> actualExpression = null;
    var mockUow = new Mock<IUnitOfWork>();
    mockUow
        .Setup(u => u.Single<Person>(It.IsAny<Expression<Func<Person, bool>>>()))
        .Callback( (Expression<Func<Person,bool>> x) => actualExpression = x);
    var uow = mockUow.Object;
    uow.Single<Person>(p => p.FirstName == "Sergi");

    Expression<Func<Person, bool>> expectedExpression = p => p.FirstName == "Sergi";

    Assert.AreEqual(expectedExpression.ToString(), actualExpression.ToString());
}

Поскольку у вас есть несколько тестовых примеров, которые не должны пройти, вероятно, у вас другая проблема.

ОБНОВЛЕНИЕ : Для вашего обновления рассмотрите следующие настройки и выражения:

string normal_type = "NORMAL";
// PersonConstants is a static class with NORMAL_TYPE defined as follows:
// public const string NORMAL_TYPE = "NORMAL";
Expression<Func<Person, bool>> expr1 = p => p.Type == normal_type;
Expression<Func<Person, bool>> expr2 = p => p.Type == PersonConstants.NORMAL_TYPE;

Одно выражение ссылается на переменную экземпляра содержащего метода. Другое представляет выражение, которое ссылается на член const статического класса. Это два разных выражения, независимо от значений, которые могут быть назначены переменным во время выполнения. Однако если string normal_type изменяется на const string normal_type, то выражения снова совпадают с каждой ссылкой a const в правой части выражения.

1 голос
/ 10 июня 2014

Я также хотел бы поделиться другим подходом к сравнению выражения параметра с ожидаемым выражением.Я искал в StackOverflow «как сравнивать выражения», и меня привели к следующим статьям:

Затем меня привели к этому хранилищу Subversion для db4o.net .В одном из своих проектов, пространстве имен Db4objects.Db4o.Linq.Expressions, они включают класс с именем ExpressionEqualityComparer.Мне удалось извлечь этот проект из репозитория, скомпилировать, собрать и создать DLL для использования в моем собственном проекте.

С помощью ExpressionEqualityComparer вы можете изменить вызов Verify на что-то вродеследующие:

session.Verify(x => x .Single(It.Is<Expression<Func<Person, bool>>>(e => new ExpressionEqualityComparer().Equals(e, expression))));

В конечном итоге методы ExpressionEqualityComparer и ToString() в этом случае возвращают значение true (при этом ToString, скорее всего, быстрее - скорость не тестировалась),Лично я предпочитаю подход сравнения, так как считаю, что он более самодокументирован и лучше отражает ваши замыслы (сравнивая объекты выражений, а не сравнивая строки их выходных данных ToString).* Я все еще ищу файл лицензии db4o.net в этом проекте, но я все равно не изменил код, включил уведомление об авторских правах и (поскольку страница общедоступна), я предполагаю, что на данный момент этого достаточно...; -)

...