Is Assert.Fail() считается плохой практикой?

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

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


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

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

@Matt Howells Отличная идея. NotImplementedException связывает намерение лучше, чем assert.Fail() в этом случае

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

Ответ 1

Для этого сценария, а не для вызова Assert.Fail, я делаю следующее (в С#/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 рекомендует разработчикам находить и использовать более явные альтернативы и поддерживать создание новых утверждений там, где это необходимо.

Ответ 2

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

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

Ответ 3

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

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

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

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

Ответ 4

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

Изменить:

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

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

Ответ 5

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

[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));
    }
}

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

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

Ответ 6

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

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

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

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

Ответ 7

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

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

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

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

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

Ответ 8

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

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")
}

Ответ 9

Остерегайтесь 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. Опять же, тест терпит неудачу, если и только если он выдает исключение.

Ответ 10

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

Ответ 11

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

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

Считаете ли вы, что используете Todo List или применяете методологию GTD к своей работе?

Ответ 12

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

Ответ 13

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

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

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

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

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

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

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

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

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

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

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