Nunit: Перехват AssertionException не проходит тесты, начиная с 3.10.

Созданный на 14 мар. 2018  ·  22Комментарии  ·  Источник: nunit/nunit

`` ''
пытаться
{
Assert.True (ложь, «сообщение об ошибке»);
}
поймать (AssertionException e)
{
Trace.WriteLine («Игнорировать.»);
}

        Trace.WriteLine("Test Passed");

`` ''

В Nunit 3.9.0 этот тест является успешным и помечается как пройденный в проводнике тестов при запуске из обозревателя тестов Visual Studio. В Nunit 3.10 тестовый набор помечен как неуспешный в обзоре обозревателя тестов. Последняя строка трассировки выполняется в обеих версиях.

question

Самый полезный комментарий

@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 в настоящее время выполняет свои собственные тесты.

Все 22 Комментарий

Ваш тест проверяет два предположения о том, как работает NUnit:

  1. Все ошибки утверждения вызывают исключение AssertionException.
  2. Этот перехват исключения предотвращает сообщение об ошибке.

Как это часто бывает, первое утверждение в основном верно, но не выполняется в нескольких блоках assert или при выдаче предупреждений. Второе совсем не соответствует действительности. К тому времени, когда вы поймаете исключение, сбой уже будет записан. Это изменение во внутреннем поведении NUnit, наблюдаемое только в том случае, если вы перехватываете эти исключения. Я, например, говорил людям не полагаться на такое поведение в течение многих лет, поскольку это просто случайность реализации.

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

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

Мы пробовали использовать для этого метод Warn.If (), однако он устанавливает результат в «Тест пропущен» в обозревателе тестов. Это вызывает сообщения об ошибках, поскольку тест вообще не пропускается. Поэтому мы решили реализовать перехват утверждения и отследить предупреждение. Я знаю, нежелательно, но это сработало очень хорошо. По крайней мере, до последнего обновления.

Мне любопытно, было ли это изменение намеренным, в этом случае я знаю, что должен что-то изменить.

Warn.If отлично работает ... __except__ в обозревателе тестов. 😢 Проблема в том, что обозреватель тестов знает о прохождении и неудаче, но не о предупреждении. Нам пришлось преобразовать наши результаты предупреждений во что-то, что они могут понять до тех пор, пока они не реализуют статус результата предупреждения. FWIW, у нас есть аналогичная проблема с нашим состоянием «Неокончательный результат».

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

Если у вас нестабильные тесты, многие годы принято игнорировать их. К сожалению, многие команды «игнорируют игнорируемые тесты», и в этом случае это не такой уж хороший выбор. Если вы можете научить свою команду серьезно относиться к предупреждениям - почти так же серьезно, как к ошибкам и сбоям, - тогда вы можете использовать статус предупреждения, чтобы выделить нестабильность. Другая возможность - использовать Игнорировать со свойством «До», чтобы он перешел в сбой, если не будет исправлен к определенной дате.

Кстати, когда я тренирую команды, у меня обычно есть постоянно обновляемый Большой видимый дисплей игнорируемых тестов. В большинстве команд все знают, кто за что отвечает, поэтому нет необходимости вызывать человека, ответственного за нестандартный тест. Они имеют тенденцию исправляться, когда все видят проблему.

@svanimpelen вы пробовали использовать атрибут Retry для нестабильных тестов? Это позволит вам запустить их несколько раз, чтобы они прошли.

Что касается предупреждения, приводящего к пропуску тестов в проводнике, мы поговорили с командой Visual Studio о разрешении нам указывать больше состояний результатов, чем ограниченное количество из MSTest, которое они в настоящее время поддерживают. Им нравится эта идея, поэтому мы надеемся, что в будущем мы увидим там улучшения. А до тех пор придется мучиться с пропущенными. Тем не менее, пропущенные результаты в предупреждениях. Я полагаю, вы хотели, чтобы предупреждения были очень очевидными 😄

Многие из наших методов документируют, что API выдает исключение AssertionException в случае ошибки:

https://github.com/nunit/nunit/blob/d03e93c8f25170a8ff48e80863a3fe6fd294613a/src/NUnitFramework/framework/Assert.That.cs#L40 -L43

Разве эта документация, плюс давнее поведение 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 в настоящее время выполняет свои собственные тесты.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги