- PVSM.RU - https://www.pvsm.ru -
Недавно была выпущена первая бета версия тестового фреймворка NUnit v3 [1]. Кроме всего прочего, эта версия реализует параллельное выполнение тестов (практически «из коробки»). Я решил проверить как это работает на одном реальном проекте и обнаружил, что новая версия nunit-а не поддерживает [2] часть используемых вещей предыдущих версий. В частности предлагается вместо аттрибута ExpectedException использовать Assert.Thorws или Assert.That.
Независимо от релиза этой беты, в одном из проектов начал использовать модель Assert.That [3] вместо всех остальных методов и атрибутов nunit-а.
Под катом небольшой опыт перевода аттрибута ExpectedException в модель Assert.That.
Как оказалось, тестовый проект, который я выбрал для перевода под nunit v3. содержит более 100 использований аттрибута ExpectedException [4]. Естественно захотелось как то автоматизировать процесс перехода.
Интересно, что если раньше аттрибут ExpectedException казался очень удачным, то в последнее время обнаружил несколько проблем:
Например, в следующем тесте не очень понятно, в какой строчке ожидается исключительная ситуация. Обычно — в последней, но если какой-то предыдущий метод выкинет эту же исключительную ситуацию, то тест будет работать не правильно. В любом случае, есть тут какая-то неопределенность «где все-таки ожидать исключительную ситуацию».
[Test]
[ExpectedException(typeof(ArgumentException))]
public void TestExpectedException()
{
foo1();
foo4();
foo1();
}
Еще одна мелочь, которая мешает, это отчеты по «покрытию кода», т.е. запускаешь dotCover [5], изучаешь отчет и видишь:

А вот заменишь на Assert.That и совсем другое дело: получаешь 100% покрытие.

После того как я понял, что менять «руками» это слишком простой способ:), решил написать плагин для решарпера, который помогает переводить конструкции nunit в модель Assert.That.
И начал я с тех, которые мне нужны для перевода моего тестового проекта.
Сперва все было довольно просто:
[Test]
[ExpectedException]
public void TestShortExpectedException()
{
foo1();
foo2();
foo1();
}
перевел в
[Test]
public void TestShortExpectedException()
{
foo1();
Assert.That(foo2, Throws.Exception);
foo1();
}
Более сложный пример потребовол использования анонимного метода
[Test]
[ExpectedException]
public void TestExpectedExceptionWithExpressions()
{
double i = 2 + getNumber();
}
[Test]
public void TestExpectedExceptionWithExpressions()
{
Assert.That(() => { double i = 2 + getNumber(); }, Throws.Exception);
}
Конкретный ожидаемый тип потребовал реализации Throws.TypeOf
[Test]
[ExpectedException(typeof(ArgumentException))]
public void TestExpectedException()
{
foo1();
foo4();
foo1();
}
[Test]
public void TestExpectedException()
{
foo1();
Assert.That(() => { foo4(); }, Throws.TypeOf<ArgumentException>());
foo1();
}
Ожидаемый текст сообщения исключительной ситауции (или по-русски «месадж эксепшина») потребовал добавить .And.Message
[Test]
[ExpectedException(typeof(NotImplementedException), ExpectedMessage = "customer message")]
public void TestExpectedExceptionWithCustomerMessage()
{
foo4("customer message");
}
[Test]
public void TestExpectedExceptionWithCustomerMessage()
{
Assert.That(() => { foo4("customer message"); }, Throws.TypeOf<NotImplementedException>().And.Message.EqualTo("customer message"));
}
Пока еще не все конструкции поддерживаются: например, MathType не будет конвертирован корректно.
[Test]
[ExpectedException(typeof(NotImplementedException), ExpectedMessage = "customer message", MatchType = MessageMatch.Contains)]
public void TestExpectedExceptionWithCustomerMessage()
{
foo4("my customer message");
}
Конвертирование конструкций Assert.IsNullOrEmpty and Assert.IsNotNullOrEmpty реализвал без программирования, а только через Custom Patterns [6].
Custom Patterns — фича сильная, но, судя по всему, в случае сложных конструкций не все еще гладко работает.
Assert — конструкция простая и проблем не было:


Плагин назвается «NUnit.That.Resharper.Plugin» и его бета версия доступна для скачивания через «Resharper — Manage Extensions».
Тестировал только на resharper-е версии 8.2.
Прямо сейчас поддерживается небольшой набор конструкций [7].
Визуально работа плагина выглядит так:
выбираешь на нужной строчке Replace

и получаешь сконвертированное выражение (аттрибут ExpectedException при этом удаляется)

Выводы:
— Assert.That мне показался довольно привлекательной моделью;
— NUnit v3. пока еще бета (осторожно с документацией!), но можно уже начинать примерять на тестовых проектах и подготавливать реальные;
— полный цикл (включая тесты и дистрибуцию) написания плагинов для решарпера вещь не такая сложная, как могло казаться, и может применяться для решения не только «общих», но и локальных проблем.
Хотел бы выразить особую благодарность команде resharper-а (и лично mezastel [8]), которые помогли вникнуть в особенности разработки плагинов. Resharper SDK дает возможность создавать проекты Visual Studio из темплейтов, что сильно облегчает дело.
Ссылки:
— проект на гитхабе https://github.com/constructor-igor/NUnit.That.Resharper.Plugin [7]
— плагин NUnit.That.Resharper.Plugin в галерии https://resharper-plugins.jetbrains.com/packages/NUnit.That.Resharper_v8.Plugin/ [9]
Ссылки на примеры и документацию
— документация для разработчика resharper-а ReSharper DevGuide [10];
— пост "Написать плагин для ReSharper — не так и сложно [11]"
— Agent Mulder plugin for ReSharper [12]
Автор: constructor
Источник [13]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/c-2/87292
Ссылки в тексте:
[1] NUnit v3: http://www.nunit.org/
[2] не поддерживает: http://nunit.org/index.php?p=breakingChanges&r=3.0
[3] Assert.That: http://www.nunit.org/index.php?p=constraintModel&r=2.4.8
[4] ExpectedException: http://www.nunit.org/index.php?p=exception&r=2.4.8
[5] dotCover: https://www.jetbrains.com/dotcover/
[6] Custom Patterns: https://www.jetbrains.com/resharper/webhelp80/Reference__Options__Code_Inspection__Custom_Patterns.html
[7] поддерживается небольшой набор конструкций: https://github.com/constructor-igor/NUnit.That.Resharper.Plugin
[8] mezastel: http://habrahabr.ru/users/mezastel/
[9] https://resharper-plugins.jetbrains.com/packages/NUnit.That.Resharper_v8.Plugin/: https://resharper-plugins.jetbrains.com/packages/NUnit.That.Resharper_v8.Plugin/
[10] ReSharper DevGuide: https://www.jetbrains.com/resharper/devguide/
[11] Написать плагин для ReSharper — не так и сложно: http://dev-ua.livejournal.com/774.html
[12] Agent Mulder plugin for ReSharper: https://github.com/hmemcpy/AgentMulder
[13] Источник: http://habrahabr.ru/post/254289/
Нажмите здесь для печати.