`` ''
пытаться
{
Assert.True (ложь, «сообщение об ошибке»);
}
поймать (AssertionException e)
{
Trace.WriteLine («Игнорировать.»);
}
Trace.WriteLine("Test Passed");
`` ''
В Nunit 3.9.0 этот тест является успешным и помечается как пройденный в проводнике тестов при запуске из обозревателя тестов Visual Studio. В Nunit 3.10 тестовый набор помечен как неуспешный в обзоре обозревателя тестов. Последняя строка трассировки выполняется в обеих версиях.
Ваш тест проверяет два предположения о том, как работает NUnit:
Как это часто бывает, первое утверждение в основном верно, но не выполняется в нескольких блоках assert или при выдаче предупреждений. Второе совсем не соответствует действительности. К тому времени, когда вы поймаете исключение, сбой уже будет записан. Это изменение во внутреннем поведении NUnit, наблюдаемое только в том случае, если вы перехватываете эти исключения. Я, например, говорил людям не полагаться на такое поведение в течение многих лет, поскольку это просто случайность реализации.
Я понимаю, что ваш пример кода предназначен только для демонстрации проблемы, которую вы видите. Можете ли вы привести пример того, что вы на самом деле пытаетесь сделать, перехватывая исключение? Я почти уверен, что мы сможем помочь вам найти лучшее решение.
Спасибо, что ответили мне за такое короткое время. Я использую этот механизм, когда все еще идет нестабильно. Поэтому иногда программное обеспечение не работает должным образом. В таких случаях я сообщаю об этом разработчикам. Тем временем мы расширили тест, чтобы обойти это нестабильное поведение. Таким образом, тест по этой проблеме проходит, а не терпит неудачу.
Мы пробовали использовать для этого метод Warn.If (), однако он устанавливает результат в «Тест пропущен» в обозревателе тестов. Это вызывает сообщения об ошибках, поскольку тест вообще не пропускается. Поэтому мы решили реализовать перехват утверждения и отследить предупреждение. Я знаю, нежелательно, но это сработало очень хорошо. По крайней мере, до последнего обновления.
Мне любопытно, было ли это изменение намеренным, в этом случае я знаю, что должен что-то изменить.
Warn.If
отлично работает ... __except__ в обозревателе тестов. 😢 Проблема в том, что обозреватель тестов знает о прохождении и неудаче, но не о предупреждении. Нам пришлось преобразовать наши результаты предупреждений во что-то, что они могут понять до тех пор, пока они не реализуют статус результата предупреждения. FWIW, у нас есть аналогичная проблема с нашим состоянием «Неокончательный результат».
Внесенное нами изменение было преднамеренным и планировалось давно. Мы не хотим полагаться на исключения так много времени, и мы хотим сообщать о результатах утверждений - включая в конечном итоге пройденные - каким-то образом, который не останавливает тестирование. Мы внесли некоторые изменения, чтобы смягчить последствия этого для таких пользователей, как вы, но, похоже, это не имело возможности смягчить последствия.
Если у вас нестабильные тесты, многие годы принято игнорировать их. К сожалению, многие команды «игнорируют игнорируемые тесты», и в этом случае это не такой уж хороший выбор. Если вы можете научить свою команду серьезно относиться к предупреждениям - почти так же серьезно, как к ошибкам и сбоям, - тогда вы можете использовать статус предупреждения, чтобы выделить нестабильность. Другая возможность - использовать Игнорировать со свойством «До», чтобы он перешел в сбой, если не будет исправлен к определенной дате.
Кстати, когда я тренирую команды, у меня обычно есть постоянно обновляемый Большой видимый дисплей игнорируемых тестов. В большинстве команд все знают, кто за что отвечает, поэтому нет необходимости вызывать человека, ответственного за нестандартный тест. Они имеют тенденцию исправляться, когда все видят проблему.
@svanimpelen вы пробовали использовать атрибут Retry
для нестабильных тестов? Это позволит вам запустить их несколько раз, чтобы они прошли.
Что касается предупреждения, приводящего к пропуску тестов в проводнике, мы поговорили с командой Visual Studio о разрешении нам указывать больше состояний результатов, чем ограниченное количество из MSTest, которое они в настоящее время поддерживают. Им нравится эта идея, поэтому мы надеемся, что в будущем мы увидим там улучшения. А до тех пор придется мучиться с пропущенными. Тем не менее, пропущенные результаты в предупреждениях. Я полагаю, вы хотели, чтобы предупреждения были очень очевидными 😄
Многие из наших методов документируют, что API выдает исключение AssertionException в случае ошибки:
Разве эта документация, плюс давнее поведение NUnit, не является критическим изменением в 3.10?
Если я чего-то не упускаю, мы должны хотя бы удалить некорректные документы.
Я согласен с тем, что документация может вводить в заблуждение теперь, когда мы ввели несколько блоков утверждений. Тем не менее, в этом случае все еще генерируется AssertionException
, разница в том, что тест теперь отображается как сбой в Visual Studio, даже если исключение было обнаружено. Я не считаю это серьезным изменением. Пользователи полагались на недокументированное поведение, которое могло работать. Для меня это ничем не отличается от того, что пользователи полагаются на тот факт, что тесты выполнялись в алфавитном порядке, чтобы упорядочить свои тесты.
Вероятно, нам следует обновить документацию. Мы могли бы удалить информацию о генерировании исключения и вместо этого просто указать, что ложное условие не проходит тест. И / или мы могли бы заявить, что исключение не генерируется в блоках с несколькими утверждениями? Лично я считаю, что тип создаваемого исключения является внутренней деталью, поэтому я предпочитаю первый вариант.
Думаю, это правильно. Если использование исключений должно быть где-либо задокументировано, это может быть в разделе wiki, посвященном внутреннему устройству.
Когда в документации метода говорится, что будет выброшено исключение, я думаю, что это однозначно говорит о том, что исключение может быть обнаружено на сайте вызова. В C # нет проверенных исключений, а XML-документы - это то, как мы передаем эту часть контракта метода. Поскольку, насколько мне известно, язык «выбрасывает X» всегда означал, что метод его не улавливает, я все еще немного беспокоюсь о том, как это восприняли другие. Может быть, заметка о критических изменениях в вики на всякий случай?
Мне нравится идея удалить все ссылки на AssertionException из XML-документов и создать вики-страницу, предупреждающую людей не использовать AssertionException.
💭Если перехват AssertionException
всегда является ошибкой, можем ли мы сделать этот тип внутренним?
@ jnm2.
Я согласен с вами в важности документов XML, но факт в том, что эти документы __not__, как мы до сих пор передавали часть контракта. На самом деле я думаю, что вы можете быть первым, кто это предложит, и я думаю, что это хорошая идея.
Но я думаю, вы должны признать, что если вы начнете думать о них таким образом, придется изменить множество ошибочных комментариев! Я не думаю, что мы можем рассматривать каждое из этих изменений документа как критическое изменение. В каждом конкретном случае мы можем разумно рассматривать любое изменение поведения как нарушение, но это другое дело. Изменения в документации не должны нарушаться.
Это поднимает вопрос о том, что должно быть в списке критических изменений. Если я понял ваши прошлые комментарии, я думаю, вы, как правило, хотите задокументировать все, что может сломать любого пользователя. Я предпочитаю документировать вещи, которые, по нашему мнению, сломают многих пользователей.
Вот как я вижу разницу - другие могут не согласиться ... Документирование __ всего, что может сломать__, для меня пахнет CYA, вроде того типа CYA, которым иногда балуется руководство, когда они сосредотачиваются на том, кого обвиняют в неудаче, а не на предотвращении самой неудачи. ЕСЛИ мы все документируем, тогда мы можем сказать ... «Видите, мы прикрыты». Если мы задокументируем только то, что считаем важным, то мы рискуем ошибиться и принести извинения, но мы даем среднему пользователю гораздо более легкий для понимания набор изменений. Я предпочитаю последнее.
Бывают случаи, когда перехватить исключение утверждения безопасно. Например, некоторое время назад я внес изменение, чтобы оно оставалось безопасным при запуске тестов напрямую, без использования TestExecutionContext. Если вы сделаете исключение внутренним, вы нарушите его. На самом деле, я был счастлив сломать его, но команда согласилась, что мы не должны этого делать, поэтому я исправил это. Вам просто нужно решить, что вы хотите сломать. Это исправление помогло __not__ решить текущую проблему, которая касается перехвата исключения внутри самого теста, а не в коде, вызывающем метод.
Перехват AssertionException в тесте - это то, что я бы назвал классическим «запахом». Вероятно, это неправильно, но чтобы узнать об этом, нужно посмотреть на конкретный случай. Я бы сказал пользователям: «Catching AssertionException, скорее всего, является ошибкой, если вы не имеете подробных знаний о внутреннем устройстве NUnit. Чтобы обеспечить правильное поведение, вы не должны пытаться изменить результат теста и должны повторно генерировать исключение после того, как сделаете то, что вы хотите сделать. с этим." Однако я до сих пор не думаю, что такая информация принадлежит документам XML или общим пользовательским документам. Я бы поместил его как страницу во внутреннем разделе.
Кстати, мой прошлый опыт заключался в том, что документирование чего-либо, а затем указание людям не использовать это приводит к увеличению использования. 😄
Хорошие моменты, спасибо! Я думаю, что частично думаю о CYA. Есть еще один элемент, который заключается в том, что как потребитель библиотек я лично был бы признателен, если бы мне сообщали о каждом критическом изменении, чтобы я мог искать неверные предположения в моем собственном коде. Особенно с такими изменениями, которые компилятор не замечает. Поскольку я был бы признателен за это, я до некоторой степени проецирую это на наших пользователей.
Должны ли мы начинать новую проблему, чтобы отслеживать исправления в документации?
Почему новый?
Название похоже на то, что мы хотели бы закрыть как ответ на вопрос, а не исправить. Будем ли мы обычно менять заголовок и использовать эту проблему для отслеживания изменения документа?
Обычно я просматриваю проблему и классифицирую ее ... в данном случае как проблему с документацией. По общему признанию, мы потратили очень много времени на то, чтобы сначала обсудить это как возможную ошибку. На самом деле это вопрос администрации проекта, и он выходит за рамки моей компетенции.
Я говорю, что делайте что-нибудь логичное и последовательное. 😄 Как вариант, закрыть это как не ошибку. Однако это отделяет жалобу от решения. Написание пояснительного комментария, когда вы реклассифицируете его как ошибку документации и, возможно, редактируете заголовок, сохраняет решение вместе с жалобой. В любом случае название проблемы «Выполнено» заканчивается в примечаниях к выпуску, поэтому оно должно отражать то, что произошло.
Можете ли вы привести пример того, что вы на самом деле пытаетесь сделать, перехватывая исключение? Я почти уверен, что мы сможем помочь вам найти лучшее решение.
В настоящее время у меня такая же проблема, так как мои тесты начали давать сбой после обновления до 3.10. Теперь вот моя проблема:
У меня есть собственные методы утверждения тестов. Те внутренне вызывают NUnit.Assert. Я тестирую эти методы утверждения, чтобы убедиться, что они действительно не срабатывают, когда я ожидаю их сбоя.
В настоящее время мои тесты ловят AssertionException
чтобы проверить, что пользовательский метод утверждения приведет к сбою теста, но после обновления все эти тесты начинают сбоить по вышеуказанным причинам.
Каков рекомендуемый способ тестирования тех, у кого установлена 3.10?
Спасибо.
Самый простой подход - использовать Assert.Throws для перехвата исключения, а не делать это самостоятельно. Assert.Throws понимает внутреннее устройство NUnit и гарантирует, что о сбое не будет сообщено.
Я видел это в своих уведомлениях на днях и не придал этому большого значения. Однако обновление моего библиотечного проекта, который выполняет Assert.That в рамках своей логики (это вспомогательная среда тестирования - специально для обеспечения простого способа проверки устойчивости EF при кодировании контекстов и сущностей EF вручную и кодировании соответствующей базы данных миграции вручную (или с помощью чего-то вроде FluentMigrator) - другими словами, он должен выполнять утверждения от имени вызывающего), теперь я получаю тесты, которые терпят неудачу там, где их не должно быть - один тест, в частности, утверждает что определенное условие должно вызывать сбои теста - что оно и происходит, но поскольку «уловка» больше не работает, как всегда, эти тесты, когда они фактически проходят, регистрируются как неуспешные.
Итак, я думаю, я не могу выдавать правильные утверждения NUnit или использовать Assert.That
в коде библиотеки, предназначенном для ускорения разработки тестов путем выполнения утомительных задач для пользователя?
Я согласен с предыдущими утверждениями о том, что, если исключение не может быть использовано пользователем, оно не должно быть доступно для создания пользователем; однако это не решает проблему, когда у меня есть код Assert.That
для потребителя и тесты, чтобы доказать, что они действительно вызывают сбой, потому что код делает то, что должен: сбой с утверждениями nunit.
Возможно, нам следует создать некоторые API-интерфейсы для получения результатов?
@fluffynuts Если я правильно вас понял, предложение catch
находится в вашем тестовом коде, а не в коде вашей пользовательской библиотеки. (Если он также находится в пользовательской библиотеке, мы можем поговорить об этом, но проблемы немного другие.)
Конечно, у NUnit есть собственные тесты, поэтому при внесении этого изменения у нас была точно такая же потенциальная проблема. Естественно, мы изменили наши собственные тесты, чтобы они продолжали проходить. Вы можете сделать то же самое.
Первая линия защиты - Assert.Throws
. В большинстве случаев при сбое тестирования вы можете напрямую заменить try / catch(AssertionException)
на Assert.Throws<AssertionException>
. Внутренне Assert.Throws
создает одноразовый изолированный TestExecutionContext
поэтому ваши фактические результаты тестирования не испорчены ошибкой, которую вы тестируете.
В некоторых случаях вам может потребоваться создать свой собственный изолированный контекст. Вы можете увидеть, как это сделать, изучив код Assert.Throws
.
Однако большая часть собственного тестирования отказов NUnit на самом деле использует другой подход. У нас есть отдельная сборка с тестами, которые запускаются нашими тестовыми программами. Мы фиксируем результат, а затем можем его изучить. Это больше работы, но позволяет избежать слишком большого количества деталей реализации NUnit в наших тестах. Большинство методов, которые это делают, можно найти в нашей сборке тестовых утилит. Я думаю, это то, что имеет в виду @ jnm2 .
Отправляйте любые вопросы, которые у вас есть, или свяжитесь со мной в автономном режиме, чтобы получить дополнительную помощь.
Спасибо за информацию. Да, утверждения улавливаются в тестовом коде. Assert.Throws обычно справляется с этой задачей, но конкретный рассматриваемый тест запускает код библиотеки со «слишком низкой» дельтой, разрешенной для тестирования значения даты и времени, которое должно быть введено и получено из базы данных. В большинстве случаев дрейф в localdb для этого составляет 1 мс, но иногда он дрейфует выше - и этот тест просто нацелен на то, чтобы подтвердить, что «иногда» случается. Итак, на данный момент я запускаю код в 4 параллельных потоках 10 раз и ожидаю как минимум одного сбоя. Assert.Throws, к сожалению, слишком детерминирован для этого случая, поэтому мне нужно либо прекратить использование исключений nunit (или Assert, в коде библиотеки), либо мне нужно изучить фу, которое вы, ребята, используете. Буду признателен за любые указатели - может произойти в автономном режиме, если хотите.
Хех, я только что понял, что сбои при тестировании насоса сообщений для моего AsyncVoidVerificationScope, на которые я смотрел, - это точно та же проблема, что и у @fluffynuts . Фактически из-за обновления NUnit 3.10, а не моего [сборка: RestoreSynchronizationContext] ITestAction. Правильно, что я игнорирую их при одновременном выполнении нескольких дел. : D
В любом случае, похоже, что я хочу обернуть вызов моего делегата в using (new TestExecutionContext.IsolatedContext())
как это делает Assert.Throws
.
Assert.Throws
и Assert.ThrowsAsync
не устанавливают изолированные контексты перед вызовом асинхронных делегатов. Почему это?
@fluffynuts Давным- давно, когда я впервые написал Assert.Catch
он просто перехватил любое исключение и не генерировал ошибку, если исключение не было сгенерировано. К сожалению, это изменилось, и у нас больше нет «нейтрального» эквивалента Assert.Throws
. Однако вы можете написать один. Вот непроверенная версия, основанная на коде в Assert.Throws
. Он обрабатывает только синхронный код, но вы можете легко расширить его для асинхронного режима, используя дополнительный код из Assert.Throws
.
`` С #
public static void Exception SafelyCatchAnNUnitException (код TestDelegate)
{
Исключение пойманоException = null;
using (new TestExecutionContext.IsolatedContext())
{
try
{
code();
}
catch (Exception ex)
{
caughtException = ex;
}
return caughtException;
}
}
`` ''
Метод вернет любое выброшенное исключение, удаляя все остатки ошибки, оставленные методами assert NUnit. Если исключение не генерируется, возвращается значение null.
Также можно использовать IsolatedContext()
на более высоком уровне в вашем коде, чтобы получить из него фактический результат теста. Это позволит вам тестировать полностью на уровне результатов тестирования, без каких-либо ссылок на исключения. Я подумал о написании библиотеки для этого типа тестирования, которая, я думаю, будет лучше, чем то, как NUnit в настоящее время выполняет свои собственные тесты.
Самый полезный комментарий
@fluffynuts Давным- давно, когда я впервые написал
Assert.Catch
он просто перехватил любое исключение и не генерировал ошибку, если исключение не было сгенерировано. К сожалению, это изменилось, и у нас больше нет «нейтрального» эквивалентаAssert.Throws
. Однако вы можете написать один. Вот непроверенная версия, основанная на коде вAssert.Throws
. Он обрабатывает только синхронный код, но вы можете легко расширить его для асинхронного режима, используя дополнительный код изAssert.Throws
.`` С #
public static void Exception SafelyCatchAnNUnitException (код TestDelegate)
{
Исключение пойманоException = null;
}
`` ''
Метод вернет любое выброшенное исключение, удаляя все остатки ошибки, оставленные методами assert NUnit. Если исключение не генерируется, возвращается значение null.
Также можно использовать
IsolatedContext()
на более высоком уровне в вашем коде, чтобы получить из него фактический результат теста. Это позволит вам тестировать полностью на уровне результатов тестирования, без каких-либо ссылок на исключения. Я подумал о написании библиотеки для этого типа тестирования, которая, я думаю, будет лучше, чем то, как NUnit в настоящее время выполняет свои собственные тесты.