Есть ли среда С# unit test, которая поддерживает произвольные выражения, а не ограниченный набор adhoc-методов?
В основном NUnit, xUnit, MbUnit, MsTest и т.п. имеют методы, аналогичные следующим:
Assert.IsGreater(a,b)
//or, a little more discoverable
Assert.That(a, Is.GreaterThan(b))
Однако существует ограниченное число таких операторов сравнения; и они дублируют языки операторов бесполезно. Когда я хочу что-нибудь даже немного сложное, например...
Assert.That(a.SequenceEquals(b))
Я часто либо оставляю копаться в руководстве, чтобы найти эквивалент выражения в NUnit-talk, либо вынужден вернуться к простым логическим утверждениям с менее полезными сообщениями об ошибках.
С#, однако, хорошо интегрируется с произвольными выражениями, поэтому должен быть возможно иметь метод со следующей сигнатурой:
void That(Expression<Func<bool>> expr);
Такой метод может использоваться как для выполнения теста (т.е. подтверждения утверждения), так и для обеспечения менее непрозрачной диагностики в случае отказа теста; в конце концов, выражение может быть представлено псевдокоду, чтобы указать, какое выражение не удалось; и с некоторым усилием вы могли бы даже разумно оценить ошибочные выражения, чтобы дать некоторое представление о значении подвыражений.
Например:
Assert.That(()=> a == b);//could inspect expression and print a and b
Assert.That(()=> a < b && b < c);
//could mention the values of "a<b" and "b<c" and/or list the values of a, b, and c.
Как минимум, это сделает использование параллельного языка для выражений ненужным, и в некоторых случаях это может сделать сообщения об ошибках более полезными.
Существует ли такая вещь?
Изменить: После попытки (и симпатии!) Power Assert я закончил переопределять его, чтобы устранить несколько ограничений. Мой вариант этого опубликован как ExpressionToCode; см. мой ответ ниже для получения списка улучшений.
Ответы
Ответ 1
(Оригинальный плакат здесь)
Мне нравится PowerAssert.NET простой синтаксис и сообщения, но у С#, который он создает, много проблем. В частности, он не поддерживает несколько функций выражения, и он не добавляет круглых скобок, если это необходимо по приоритету/ассоциативности операторов. После исправления нескольких ошибок (и сообщения их автору) я обнаружил, что было бы проще исправить другой подход и переопределить его с нуля.
Использование похоже:
PAssert.That(()=>
Enumerable.Range(0,1000).ToDictionary(i=>"n"+i)["n3"].ToString()
== (3.5).ToString()
);
Выходы:
PAssert.That failed for:
Enumerable.Range(0, 1000).ToDictionary(i => "n" + (object)i)["n3"].ToString() == 3.5.ToString()
| | | | | |
| | | | | "3.5"
| | | | false
| | | "3"
| | 3
| {[n0, 0], [n1, 1], [n2, 2], [n3, 3], [n4, 4], [n5, 5], [n6, 6], [n7, 7], [n8, 8], [n9, 9], ...}
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ...}
Улучшения над PowerAssert.NET:
- Поддерживает доступ к статическому полю и свойствам.
- Поддерживает больше операторов, таких как логическое и побитовое отрицание.
- Распознает использование индексатора С# (например,
dict["mykey"]==3
)
- Добавляет скобки, где требуется приоритет и ассоциативность операторов (например,
() => x - (a - b) + x * (a + b)
правильно регенерируется)
- Генерирует действительные числовые и другие константные литералы, включая экраны и суффиксы, соответствующие типу выражения (например,
1m + (decimal)Math.Sqrt(1.41)
)
- Поддерживает синтаксический сахар С# для инициализаторов объектов, инициализаторов элементов, инициализаторов списков, методов расширения, среди прочего.
- Использует те же правила, что и Visual Studio по умолчанию.
- Поддержка вложенных Lambdas
- Развертывает экземпляры типичного типа в обычный С#; например
Func<int, bool>
- Поддерживает несколько конструкций дерева выражений, еще не используемых встроенными выражениями С# 4.0.
Результирующий проект (с модульными тестами) размещен в коде google под именем ExpressionToCode - я надеюсь, что это полезно для других.
Ответ 2
Проверьте библиотеку PowerAssert (пример ниже):
PAssert.IsTrue(() => x + 5 == d.Month * y);
System.Exception : IsTrue failed, expression was:
x + 5 == d.Month * y
| | | | | | |
| | | | | | 6
| | | | | 18
| | | | 3
| | | 01/03/2010 00:00:00
| | False
| 16
11
http://powerassert.codeplex.com/
Ответ 3
http://satisfyr.codeplex.com/
Использует лямбда-выражения точно так, как вы описали. Вы даже не принимаете двоичную зависимость, просто добавьте один исходный файл, соответствующий вашей инфраструктуре unit test.
Ответ 4
На самом деле существует очень веская причина, что NUnit предоставляет собственный DSL, а не обычные выражения С#. Это значит, что NUnit должен работать с любым языком .NET
используя тот же синтаксис. Это не значит, что у нас не может быть лямбда, просто мы никогда не будем
полагаются исключительно на какую-либо особенность языка.
Многие из приведенных идей будут работать, и многие сторонние программные решения могут быть включены в NUnit при условии, что их авторы захотят их предложить. Конечно, многие люди предпочитают, чтобы их решения были раздельными и что ОК тоже. Но поговорите со своими любимыми авторами, если вы хотите, чтобы они более тесно сотрудничали с NUnit.
В NUnit 2.5 вы можете использовать PredicateConstraint, который принимает лямбда в качестве аргумента. Однако синтаксис является немного ограничивающим. Ключевое слово Matches будет работать в середине выражения, поэтому вы можете написать...
Assert.That(someActual, Not.Matches(someLambda);
но делать это без необходимости...
Assert.That(someActual, новый PredicateConstraint (someLambda));
И, конечно, ни то, ни другое не так чисто, как предлагаемый синтаксис.
Все, кто интересуется этой проблемой, могут присоединиться к нам на nunit-обсуждении, где обсуждения о том, что должно быть в NUnit, фактически приводят к действию!
Charlie
Ответ 5
Конус (https://github.com/drunkcod/cone) - это дополнение NUnit, которое работает с 2.5.5 и 2.5.7 (другие версии только перекомпилируются), который дает вам эту возможность наряду с несколькими другими замечательными функциями.
Ответ 6
Никто из тех, кого я знаю, но я думаю, что это может быть добавлено в предстоящий список желаний NUnit 3.
Некоторая работа с этой целью, сделанная в #TestEx http://sharptestex.codeplex.com/, рассматривается для включения, но вы можете добавить проект/проблему для более общий подход, который вы задаете.
https://blueprints.launchpad.net/nunit-3.0
https://bugs.launchpad.net/nunit-3.0
Ответ 7
В инфраструктуре Visual Studio 2010 unit test есть класс CollectionAssert, который полезен.
Он также предоставляет Assert.IsTrue(bool) для общих случаев, которые вы создаете самостоятельно, но не используете выражения,