Автор: Sergey Teplyakov

xUnit - это самый популярный нынче тестовый фреймворк, который сейчас активно используется во многих open source проектах типа Roslyn, CoreFx и сотнях проектах поменьше. Я всю свою сознательную жизнь использовал NUnit, но сейчас, иногда по работе, иногда просто при работе с чужеродными кодовыми базами, мне приходится иметь дело с xUnit.

Поскольку тестовый фреймворк это не особый рокет сайенс, то столкнуться со сложностями при его использовании вообще довольно сложно. Конечно, если это не MSTest, который вызывает состояние недоумения с завидным постоянством.

Но несмотря на зрелость и всеобщую популярность, иногда таки посещают мысли вида WAT при работе с xUnit. Есть два конкретных момента, которые вгоняют меня в легкий ступор. Возможно, ступор этот обусловлен небольшим опытом работы с этим чудом, или неумением его готовить, но все же.

Итак, начнем.

1. Отсутствие годной документации

Нет, ну серьезно. Попробуйте найти официальную информацию о параметризованных тестах, о других атрибутах или о модели расширения (вот это хз, я не искал, просто добавил ради убедительности своих аргументов,)).

Поскольку я пользуюсь параметризованными тестами довольно часто, то был удивлен, что гугл как-то не сильно знает толковых материалов по этой теме. Или, например, попробуйте найти информацию о том, что такое CollectionDefinitionAttribute и с чем его едят. Не просто скудный пример из официальной документации, а что-то вменяемое.

Подобное отношение к документации для любого open source проекта весьма объяснимо, но для столь популярного тестового фреймворка такое отношение немного странно.

2. Отсутствие возможности указать сообщение в утверждении

Собственно, это главная проблема с моей точки зрения.

В любом тестовом фреймворке есть возможность указать кастомное сообщение в методах Assert, в любом, но не в xUnit. Точнее, не так. Вы можете это сделать только в двух случаях в методах Assert.True и Assert.False:// Декларация метода string method = @'public void Foo() {}', var contractBlock = ContractBlock.GetContractBlockFor(method), // Нет возможности указать сообщение Assert.NotNull(contractBlock), // Можно только так Assert.True(contractBlock != null, 'Contract block should not be null'),

Таким образом, сейчас вы сталкиваетесь перед выбором: хотите ли вы увидеть информативное сообщение о нарушенном утверждении (типа ждали 1 получили 42), или вы хотите видеть свое кастомное сообщение но со стандартным префиксом ждали true, получили false. И ни в одном из этих случаев вы не получите (ИМХО) того, что хотите, т.е. всей информации целиком:

clip_image001

Авторам xUnit несколько раз говорили о том, что было бы здорово иметь перегрузку методов Assert с указанием сообщений, на что был получен следующий ответ:

We are a believer in self-documenting code, that includes your assertions. If you cannot read the assertion and understand what you're asserting and why, then the code needs to be made clearer. Assertions with messages are like giving up on clear code in favor of comments, and with all the requisite danger: if you change the assert but not the message, then it leads you astray.

Взято из: https://github.com/xunit/xunit/issues/350

Мне эта логика абсолютно не понятна. Возможно, авторы xUnit хотят пойти еще дальше и запретить использовать более одного утверждения в тесте? Да, тестировать сразу 50 вещей одновременно это плохо, но вполне нормально, что для тестирования одного аспекта вам понадобится более одного вызова метода Assert.

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

Автор xUnit считает, что в сообщении может указываться лишь информация, которая и так продублирована в самом утверждении, но это не так. Я использую сообщения в методах Assert не только для документации кода, но и для вывода важной информации о состоянии системы, которая может быть использована прямо сейчас для диагностики проблемы!

Вот пример, когда такой подход очень полезен.

Есть довольно популярный паттерн обработки ошибок под названием Notification Pattern. Идея его в том, что если ошибка является чем-то ожидаемым, то вместо генерации исключений нужно возвращать некоторый кастомный объект, который и будет содержать информацию о проблеме (или проблемах). Этот подход применим довольно часто, например, для валидации данных на UI, валидации во время парсинга входных данных (например, JSON или XML данных) или вывода сообщений ошибок компиляции.

Вот пример из тестовой инфраструктуры проекта ErrorProne.NET: метод NoDiagnostic из класса AnalyzerTestFixture проверяет, что в процессе парсинга кода не было ни одной диагностики, а если же они были, то все эти диагностики выводятся в сообщении об ошибке:

protected void NoDiagnostic( Document document, string diagnosticId, ProcessedCode processedDocument) { var diagnostics = GetDiagnostics(document), string diagnosticMessage = string.Join('\r\n', diagnostics.Select(d =>, d.ToString())), Assert.That(diagnostics.Count(d =>, d.Id == diagnosticId), Is.EqualTo(0), $'Expected no diagnostics, but got some:\r\n{diagnosticMessage}'), } 

Теперь, если один из тестов будет падать из-за наличия предупреждений компилятора или других диагностик, то вместо малоинформативного сообщения Expected 0 but got 1, пользователь сможет увидеть текст полученной диагностики на экране:

clip_image002

Подобный подход позволяет оставаться в контексте решаемой задачи и приступать к фиксу кода лишь после беглого взгляда на output работы тестов. При работе с xUnit я заметил, что отсутствие необходимой информации приводит к необходимости запуска теста в режиме отладки, что серьезно сбивает процесс сделал написал тест запустил увидел проблему пофиксил.

Сообщение в утверждениях это не просто дублирование информации, оно может содержать важную информации о входных данных или состоянии тестируемого класса!

Заключение

Xunit это очень толковый и зрелый тул. Он умеет 100 500 разных штук, но, ИМХО, проваливается на базовом кейсе. Особенно настораживает отношение авторов библиотеки к фидбеку пользователей: тикет, который я приводил выше, заморожен и вы не можете добавлять туда комментарии. А любой новый, я почему-то уверен, будет закрыт, как дубликат. И я бы понял принципиальность авторов, если бы речь шла о действительно важных вещах, или о вещах, которые будут провоцировать плохой код: но я искренне не понимаю, какой вред может нанести дополнительный аргумент в методах Assert, который явно бывает полезен в ряде сценариев.

З.Ы. А вы каким тестовым фреймворком пользуетесь и что в нем вам в нем нравится/не нравится?

Помогла статья? Оцените её!
0 из 5. Общее количество голосов - 0
 

You have no rights to post comments

Дмитрий Крикунов

Публикую статьи, обучающие курсы и новости по программированию: алгоритмам, языкам (С++, Java), параллельному программированию, паттернам и библиотекам (Qt, boost).