Как проверить, что выражение замкнуто - PullRequest
4 голосов
/ 30 ноября 2009

У меня есть метод расширения со следующей подписью:

public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
{
    ...
}

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

Теперь я хотел бы написать еще один тест-кейс, который просто гарантирует, что метод использует версию с коротким замыканием and. Любая подсказка, как я могу это сделать?

Я думал, что смогу сделать что-то вроде этого:

    [Test]
    public void And_PredicatesAreShortCircuited()
    {
        var predicateNotUsed = true;
        Expression<Func<int, bool>> a = x => false;
        Expression<Func<int, bool>> b = x =>
            {
                predicateNotUsed = false;
                return true;
            };

        var foo = new[] { 1, 2, 3, 4, 5, 6, 7 }
            .Where(a.And(b).Compile())
            .ToArray();

        Assert.That(predicateNotUsed);
    }

Но я получаю гигантский красный волнистый знак под всем этим телом оператора для b, заявляющего, что "Лямбда-выражение с телом оператора не может быть преобразовано в дерево выражений" . Итак ... есть варианты? Или это невозможно написать тест?

1 Ответ

8 голосов
/ 30 ноября 2009

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

[Test]
public void And_PredicatesAreShortCircuited()
{
    Expression<Func<string, bool>> a = x => false;
    Expression<Func<string, bool>> b = x => x.Length > 10;

    var foo = new[] { null, null }
        .Where(a.And(b).Compile())
        .ToArray();
}

Другой альтернативой может быть использование некоторой побочной функции для входных данных (например, передача чего-то, что может быть изменено деревом выражений), но я думаю, что приведенное выше, вероятно, будет самым простым подходом:)

Или другая идея:

public T NonVoidFail(T x)
{
    Assert.Fail("I shouldn't be called");
    return x; // Will never happen
}

Тогда:

[Test]
public void And_PredicatesAreShortCircuited()
{
    Expression<Func<int, bool>> a = x => false;
    Expression<Func<int, bool>> b = x => NonVoidFail(x);

    var foo = new[] { 1, 2, 3, 4, 5, 6, 7 }
        .Where(a.And(b).Compile())
        .ToArray();
}

Это тот же принцип, но он даст вам более приятное исключение:)

...