Вопрос

Я часто использую Assert.Fail при выполнении TDD.Обычно я работаю над одним тестом за раз, но когда у меня появляются идеи о том, что я хочу реализовать позже, я быстро пишу пустой тест, где имя тестового метода указывает, что я хочу реализовать, в виде списка задач.Чтобы убедиться, что я не забыл, я поместил Assert.Fail() в тело.

При тестировании xUnit.Net я обнаружил, что они не реализовали Assert.Fail.Конечно, вы всегда можете Assert.IsTrue(false), но это также не передает мои намерения.У меня сложилось впечатление, что Assert.Fail не был реализован специально.Считается ли это плохой практикой?Если да, то почему?


@Martin Meredith Это не совсем то, что я делаю.Сначала я пишу тест, а затем реализую код, чтобы он работал.Обычно я думаю сразу о нескольких тестах.Или я думаю о тесте, который нужно написать, когда работаю над чем-то другим.Именно тогда я пишу пустой провальный тест, чтобы запомнить.К тому времени, когда я приступаю к написанию теста, я аккуратно работаю над тестом.

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

@Matt Howells отличная идея.В этом случае NotImplementedException сообщает о намерении лучше, чем Assert.Fail().

@Mitch пшеница это то, что я искал.Кажется, это было исключено, чтобы предотвратить другие злоупотребления, которыми я злоупотребляю.

Это было полезно?

Решение

В этом сценарии вместо вызова Assert.Fail я делаю следующее (в C#/NUnit)

[Test]
public void MyClassDoesSomething()
{
    throw new NotImplementedException();
}

Это более явно, чем Assert.Fail.

Кажется, существует общее мнение, что предпочтительнее использовать более явные утверждения, чем Assert.Fail().Однако большинство фреймворков должны включать его, поскольку они не предлагают лучшей альтернативы.Например, NUnit (и другие) предоставляют атрибут ExpectedExceptionAttribute для проверки того, что некоторый код генерирует исключение определенного класса.Однако для проверки того, что свойству исключения присвоено определенное значение, его нельзя использовать.Вместо этого вам придется прибегнуть к Assert.Fail:

[Test]
public void ThrowsExceptionCorrectly()
{
    const string BAD_INPUT = "bad input";
    try
    {
        new MyClass().DoSomething(BAD_INPUT);
        Assert.Fail("No exception was thrown");
    }
    catch (MyCustomException ex)
    {
         Assert.AreEqual(BAD_INPUT, ex.InputString); 
    }
}

Метод xUnit.Net Assert.Throws делает это намного проще, не требуя метода Assert.Fail.Не включая метод Assert.Fail(), xUnit.Net поощряет разработчиков находить и использовать более явные альтернативы, а также поддерживать создание новых утверждений там, где это необходимо.

Другие советы

Это намеренно было опущено.Это ответ Брэда Уилсона на вопрос, почему нет Assert.Fail():

На самом деле мы этого не упустили из виду.Я считаю Assert.fail - это костыль, который подразумевает, что, вероятно, отсутствует утверждение.Иногда это просто то, как структурирован тест, а иногда и потому, что Assert может использовать другое утверждение.

Я всегда использовал Assert.Fail() для обработки случаев, когда вы обнаружили, что тест должен завершиться неудачей из-за логики, выходящей за рамки простого сравнения значений.В качестве примера:

try 
{
  // Some code that should throw ExceptionX
  Assert.Fail("ExceptionX should be thrown")
} 
catch ( ExceptionX ex ) 
{
  // test passed
}

Таким образом, отсутствие Assert.Fail() в фреймворке кажется мне ошибкой.Я бы предложил исправить класс Assert, включив в него метод Fail(), а затем отправить исправление разработчикам платформы вместе с обоснованием его добавления.

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

Я использую MbUnit для модульного тестирования.У них есть возможность игнорировать тесты, которые отображаются в наборе тестов оранжевым (а не зеленым или красным).Возможно, в xUnit есть что-то похожее, и это будет означать, что вам даже не нужно добавлять какие-либо утверждения в метод, потому что он будет отображаться в раздражающе другом цвете, и его будет трудно не заметить?

Редактировать:

В MbUnit это происходит следующим образом:

[Test]
[Ignore]
public void YourTest()
{ } 

Это шаблон, который я использую при написании теста для кода, в котором я хочу выдать исключение:

[TestMethod]
public void TestForException()
{
    Exception _Exception = null;

    try
    {
        //Code that I expect to throw the exception.
        MyClass _MyClass = null;
        _MyClass.SomeMethod();
        //Code that I expect to throw the exception.
    }
    catch(Exception _ThrownException)
    {   
        _Exception = _ThrownException
    }
    finally
    {
        Assert.IsNotNull(_Exception);
        //Replace NullReferenceException with expected exception.
        Assert.IsInstanceOfType(_Exception, typeof(NullReferenceException));
    }
}

ИМХО, это лучший способ тестирования исключений по сравнению с использованием Assert.Fail().Причина этого в том, что я не только проверяю возникновение исключения вообще, но также проверяю тип исключения.Я понимаю, что это похоже на ответ Мэтта Хауэллса, но, по моему мнению, использование блокаfinally более надежно.

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

Лично у меня нет проблем с использованием набора тестов в качестве списка задач, подобного этому, если вы в конечном итоге найдёте время написать тест. до вы реализуете код для передачи.

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

Кстати, в MSTest стандартный шаблон теста использует Assert.Inconclusive в конце своих образцов.

AFAIK, платформа xUnit.NET задумана как чрезвычайно легкая, и да, они намеренно вырезали Fail, чтобы побудить разработчика использовать явное условие сбоя.

Грубое предположение:Удержание Assert.Fail предназначено для того, чтобы вы не думали, что хороший способ написания тестового кода — это огромная куча спагетти, приводящая к Assert.Fail в плохих случаях.[Изменить, чтобы добавить:ответы других людей в целом это подтверждают, но с цитатами]

Поскольку вы это не делаете, возможно, xUnit.Net чрезмерно защищает.

Или, может быть, они просто думают, что это настолько редко и настолько неортогонально, что в этом нет необходимости.

Я предпочитаю реализовать функцию под названием ThisCodeHasNotBeenWrittenYet (на самом деле что-то короче, для удобства ввода).Невозможно более четко передать намерение, а у вас есть точный поисковый запрос.

Независимо от того, не удалось ли это сделать, или оно не реализовано (чтобы спровоцировать ошибку компоновщика), или макрос не компилируется, это можно изменить в соответствии с вашими текущими предпочтениями.Например, если вы хотите запустить что-то, что является закончил, ты хочешь провала.Когда вы собираетесь избавиться от них всех, вам может понадобиться ошибка компиляции.

С хорошим кодом я обычно делаю:

void goodCode() {
     // TODO void goodCode()
     throw new NotSupportedOperationException("void goodCode()");
}

С тестовым кодом я обычно делаю:

@Test
void testSomething() {
     // TODO void test Something
     Assert.assert("Some descriptive text about what to test")
}

Если вы используете JUnit и хотите получить не сбой, а ошибку, то я обычно делаю:

@Test
void testSomething() {
     // TODO void test Something
     throw new NotSupportedOperationException("Some descriptive text about what to test")
}

Остерегаться Assert.Fail и его развращающее влияние, заставляющее разработчиков писать глупые или неработающие тесты.Например:

[TestMethod]
public void TestWork()
{
    try {
        Work();
    }
    catch {
        Assert.Fail();
    }
}

Это глупо, потому что try-catch избыточен.Тест не пройден, если он выдает исключение.

Также

[TestMethod]
public void TestDivide()
{
    try {
        Divide(5,0);
        Assert.Fail();
    } catch { }
}

Это не работает, тест всегда будет пройден независимо от результата функции Divide.Опять же, тест не пройден тогда и только тогда, когда он генерирует исключение.

Зачем вам использовать Assert.Fail, чтобы сказать, что исключение должно быть выброшено?В этом нет необходимости.Почему бы просто не использовать атрибут ExpectedException?

Если вы пишете тест, который просто проваливается, а затем пишете для него код, тогда пишете тест.Это не разработка через тестирование.

Технически Assert.fail() не нужен, если вы правильно используете разработку через тестирование.

Задумывались ли вы об использовании списка дел или о применении методологии GTD в своей работе?

MS Test имеет Утверждение.Фаил() но оно также имеет Утверждать.Безрезультатно().Я думаю, что наиболее подходящее использование Assert.Fail() — это если у вас есть встроенная логика, которую было бы неудобно вставлять в утверждение, хотя я даже не могу придумать ни одного хорошего примера.По большей части, если платформа тестирования поддерживает что-то кроме Assert.Fail(), используйте это.

Я думаю, вам следует спросить себя, что должно делать (предварительное) тестирование.

Сначала вы пишете (набор) тестов без реализации.Возможно, также сценарии на черный день.

Все эти тесты должны провалиться, чтобы быть корректными:Итак, вы хотите добиться двух вещей:1) Убедитесь, что ваша реализация правильна;2) Убедитесь, что ваши модульные тесты верны.

Теперь, если вы заранее выполняете TDD, вам нужно выполнить все ваши тесты, а также части NYI.Результат вашего общего тестового запуска считается успешным, если:1) Все реализованные вещи достигают успеха 2) Все вещи NYI не удалены

В конце концов, если ваши модульные тесты пройдут успешно, а реализации нет, это будет пропуском модульного тестирования, не так ли?

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

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

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

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

Идея состоит в том, чтобы разделить ваши тесты на несколько сборок, использовать группировку тестов (упорядоченные тесты в mstest могут справиться с этой задачей).

Тем не менее, сборка CI, которая отправляет по почте, если не все тесты в отделении NYI провалены, непроста и понятна.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top