Ответ 1
Это был бы официальный способ сделать это:
// Act
Exception ex = Record.Exception(() => someCode());
// Assert
Assert.Null(ex);
Иногда я нахожусь на unit test, который ничего не утверждает. В конкретном примере, который я натолкнул сегодня утром, было проверено, что файл журнала был записан, когда было выполнено условие. Предполагалось, что если ошибка не была пропущена, тест прошел.
У меня лично нет проблем с этим, но, похоже, это немного "запах кода", чтобы написать unit test, который не имеет никаких утверждений, связанных с ним.
Просто интересно, что люди видят на этом?
Это был бы официальный способ сделать это:
// Act
Exception ex = Record.Exception(() => someCode());
// Assert
Assert.Null(ex);
Это просто очень минимальный тест и должен быть документирован как таковой. Он только проверяет, что он не взрывается при запуске. Худшая часть таких тестов заключается в том, что они представляют ложное чувство безопасности. Ваше покрытие кода будет расти, но оно иллюзорно. Очень плохой запах.
Если утверждение отсутствует, это не тест.
Отказаться от лени - может потребоваться немного времени, чтобы выяснить, как получить утверждение там, но стоит того, чтобы знать, что он сделал то, что вы ожидали от него.
Такой тест пахнет. Он должен проверить, что файл был записан, по крайней мере, что модифицированное время было возможно обновлено.
Я видел довольно много тестов, написанных таким образом, что в итоге ничего не тестировалось, т.е. код не работал, но он тоже не взорвался.
Если у вас есть явное требование, чтобы тестируемый код не генерировал исключение, и вы хотите явно вызвать этот факт (тесты как требования docs), тогда я бы сделал что-то вроде этого:
try
{
unitUnderTest.DoWork()
}
catch
{
Assert.Fail("code should never throw exceptions but failed with ...")
}
... но это все еще немного пахнет мне, возможно, потому, что он пытается доказать отрицательный результат.
Они известны как тесты дыма и являются общими. Это основные проверки здравомыслия. Но они не должны быть единственными типами тестов, которые у вас есть. В другом тесте вам все равно потребуется какая-то проверка.
В некотором смысле вы делаете неявное утверждение - что код не генерирует исключение. Конечно, было бы более полезно фактически захватить файл и найти соответствующую строку, но я полагаю, что что-то лучше, чем ничего.
В общем, я вижу, что это происходит в интеграционном тестировании, только тот факт, что что-то удалось завершить, достаточно хорошо. В этом случае Im прохладно с этим.
Я думаю, если бы я снова и снова видел это в модульных тестах, мне было бы интересно узнать, насколько полезны тесты на самом деле.
EDIT: В примере, заданном OP, есть некоторый проверяемый результат (результат файла журнала), поэтому, если не было сделано никаких ошибок, то, что он работал, ленив.
Это может быть хорошим прагматическим решением, особенно если альтернатива вообще не является испытанием.
Проблема в том, что тест прошел бы, если бы все вызванные функции были не-ops. Но иногда просто невозможно проверить, что побочные эффекты - то, что вы ожидали. В идеальном мире было бы достаточно времени, чтобы написать проверки для каждого теста... но я там не живу.
Другое место, где я использовал этот шаблон, - это встраивание некоторых тестов производительности в модульные тесты, потому что это был простой способ заставить их запускать каждую сборку. Тесты не утверждают ничего, но измеряют, сколько времени прошло испытание и зарегистрировали это.
Я видел что-то подобное раньше, и я думаю, что это было сделано только для поддержки номеров покрытия кода. Вероятно, это не действительно тестирование поведения кода. В любом случае я согласен с тем, что он (намерение) должен быть задокументирован в тесте для ясности.
Имя теста должно документировать это.
void TestLogDoesNotThrowException(void) {
log("blah blah");
}
Как тест проверяет, записан ли журнал без утверждения?
Я должен признать, что я никогда не писал unit test, который подтвердил, что я правильно регистрирую. Но я подумал об этом и наткнулся на это обсуждение о том, как это можно сделать с JUnit и Log4J. Это не слишком красиво, но похоже, что это сработает.
Тесты всегда должны что-то утверждать, в противном случае, что вы доказываете и как вы можете последовательно воспроизводить доказательства того, что ваш код работает?
Мы делаем это все время. Мы издеваемся над нашими зависимостями, используя JMock, поэтому я предполагаю, что в некотором смысле структура JMock делает для нас утверждение... но это происходит примерно так. У нас есть контроллер, который мы хотим проверить:
Class Controller {
private Validator validator;
public void control(){
validator.validate;
}
public setValidator(Validator validator){ this.validator = validator; }
}
Теперь, когда мы тестируем Controller, мы не хотим тестировать Validator, потому что у него есть собственные тесты. поэтому у нас есть тест с JMock, чтобы убедиться, что мы вызываем подтверждение:
public void testControlShouldCallValidate(){
mockValidator.expects(once()).method("validate");
controller.control;
}
И это все, нет никакого "утверждения", но когда вы вызываете элемент управления, а метод "validate" не вызывается, среда JMock генерирует вам исключение (что-то вроде "ожидаемого метода, не вызываемого" или что-то еще).
У нас есть все это место. Это немного назад, поскольку вы в основном настраиваете свое утверждение THEN, чтобы сделать вызов тестируемого метода.
Я иногда использую свою модульную схему выбора (NUnit) для создания методов, которые действуют как точки входа в определенные части моего кода. Эти методы полезны для профилирования производительности, потребления памяти и потребления ресурсов подмножества кода.
Эти методы, безусловно, не являются модульными тестами (даже если они отмечены атрибутом [Test]) и всегда помечены как игнорируемые и явно документированные, когда они проверены в исходном элементе управления.
Я также иногда использую эти методы как точки входа для отладчика Visual Studio. Я использую Resharper, чтобы перейти непосредственно к тесту, а затем в код, который я хочу отлаживать. Эти методы либо не доводят до исходного контроля, либо приобретают их собственные утверждения.
Мои "настоящие" модульные тесты строятся во время обычных циклов TDD, и они всегда что-то утверждают, хотя и не всегда напрямую - иногда утверждения являются частью насмешливой структуры, и иногда я могу реорганизовать подобные утверждения в один метод. Имена этих реорганизованных методов всегда начинаются с префикса "Assert", чтобы сделать это очевидным для меня.