Существует ли структура модульного теста C #, которая поддерживает произвольные выражения, а не ограниченный набор методов adhoc? - PullRequest
22 голосов
/ 01 декабря 2010

В основном 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-говорящем, либо вынужден вернуться к простым логическим утверждениям с менее полезными сообщениями об ошибках.

C #, тем не менее, хорошо интегрируется с произвольными выражениями - поэтому должна быть возможность иметь метод со следующей сигнатурой:

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 ; см. мой ответ ниже для списка улучшений.

Ответы [ 7 ]

11 голосов
/ 01 декабря 2010

Проверьте библиотеку 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/

5 голосов
/ 02 декабря 2010

На самом деле есть очень веская причина, по которой NUnit предоставляет собственный DSL, а не использует обычные выражения C #.Дело в том, что NUnit должен работать с любым .NET языком, используя тот же синтаксис.Это не значит, что у нас не может быть лямбд, просто мы никогда не будем полагаться исключительно на какую-либо конкретную языковую функцию.

Многие из приведенных идей будут работать, и многие сторонние программные решения могут бытьвключены в NUnit, при условии, что их авторы хотят предложить им.Конечно, многие люди предпочитают хранить свои решения отдельно, и это тоже нормально.Но поговорите с вашими любимыми авторами, если вы хотите, чтобы они более тесно сотрудничали с NUnit.

В NUnit 2.5 вы можете использовать PredicateConstraint, который принимает лямбду в качестве аргумента.Тем не менее, синтаксис немного ограничивает.Ключевое слово Matches будет работать в середине выражения, поэтому вы можете написать ...

Assert.That (someActual, Not.Matches (someLambda);

, но делать это без Notтребует ...

Assert.That (someActual, new PredicateConstraint (someLambda));

И, конечно, ни один из них не является настолько чистым, как предлагаемый синтаксис.

Все, кто интересуется этим вопросом, могут присоединиться к нам в nunit-обсудить, где дискуссии о том, что должно быть в NUnit, фактически приведут к действию!

Чарли

5 голосов
/ 01 декабря 2010

http://satisfyr.codeplex.com/

Использует лямбда-выражения в точности так, как вы описали.Вы даже не берете бинарную зависимость, просто добавляете один исходный файл, соответствующий вашей платформе модульного теста.

2 голосов
/ 07 декабря 2010

(Оригинальный плакат здесь)

Мне нравится PowerAssert.NET , простой синтаксис и сообщения, но создаваемый им C # имеет много проблем.В частности, он не поддерживает несколько функций выражений и не добавляет круглые скобки, если этого требует приоритет / ассоциативность операторов.После исправления нескольких ошибок (и сообщения о них автору) я обнаружил, что было бы проще исправить с помощью другого подхода и переопределить его с нуля.

Использование аналогично:

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 :

  • Поддерживает статические поля и доступ к свойствам
  • Поддерживает больше операторов, таких как логическое и побитовое отрицание.
  • Распознает использование индексатора C # (например, dict["mykey"]==3)
  • Добавляет скобки, гдетребуется для приоритета оператора и ассоциативности (например, () => x - (a - b) + x * (a + b) корректно генерируется)
  • Генерирует допустимые числовые и другие константы, включая экранированные символы и суффиксы, в зависимости от типа выражения (например, 1m + (decimal)Math.Sqrt(1.41))
  • Поддерживает синтаксический сахар C # для инициализаторов объектов, инициализаторов членов объектов, инициализаторов списков, методов расширения, среди прочего.
  • Использует те же правила интервалов, которые Visual Studio делает по умолчанию.
  • Поддерживает вложенные лямбда-выражения
  • Расширяет экземпляры универсального типа в обычный C #;например, Func<int, bool>
  • Поддерживает несколько конструкций дерева выражений, еще не используемых встроенными выражениями C # 4.0.

Полученный проект (с юнит-тестами) размещается в коде Google под именем ExpressionToCode - надеюсь, он будет полезен другим.

2 голосов
/ 01 декабря 2010

Cone (https://github.com/drunkcod/cone) - это надстройка NUnit, которая работает с 2.5.5 и 2.5.7 (другие версии только перекомпилированы), которая предоставляет вам эту возможность вместе с несколькими другими изящными функциями.

1 голос
/ 01 декабря 2010

Никто из тех, кого я знаю, но я думаю, что это можно добавить в предстоящий список пожеланий NUnit 3.

В этой связи рассматривается некоторая работа, выполненная в #TestEx http://sharptestex.codeplex.com/, но вы можете добавить план / проблему для более общего подхода, который вы запрашиваете.

https://blueprints.launchpad.net/nunit-3.0

https://bugs.launchpad.net/nunit-3.0

0 голосов
/ 01 декабря 2010

В модуле модульного тестирования Visual Studio 2010 есть класс CollectionAssert, который является полезным.

Он также предоставляет Assert.IsTrue (bool) для общих случаев, которые вы создаете сами, но ни один из них не использует выражения;

...