Есть ли необходимость сохранять тесты для простых (автономных) функций?

36

Учти это:

public function polynominal($a, $b, $c, $d)
{
    return  $a * pow($x, 3) + $b * pow($x, 2) + $c * $x + $d;
}

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

Почему бы тогда не удалить эти тесты и жить долго и счастливо? Я хочу сказать, что некоторые функции не нужно тестировать постоянно после того, как было доказано, что они работают. Я ищу контрапункты, которые заявляют, да, эти функции все еще должны быть проверены, потому что: ... Или что да, они не должны быть проверены ...

Деннис
источник
32
Разве не верно для каждой функции, что если вы не измените свой код, если он работал вчера, он будет работать и завтра? Дело в том , что программное обеспечение будет изменено.
5gon12eder
3
Потому что никто никогда не напишет override_function('pow', '$a,$b', 'return $a * $b;');в здравом уме ... и не попытается переписать его для обработки сложных чисел.
8
Итак ... хм ... этот "проверенный код без ошибок" ... в нем есть ошибка .
Для небольших функций, подобных этим, вы можете рассмотреть тестирование на основе свойств. Тестирование на основе свойств автоматически создает тестовые случаи и тесты для предварительно определенных инвариантов. Они предоставляют документацию через инварианты, что делает их полезными для хранения. Для простых функций, подобных этой, они идеально подходят.
Мартейн
6
Кроме того, эта функция является отличным кандидатом на изменение формы Хорнера, (($a*$x + $b)*$x + $c)*$x + $dкоторую легко ошибиться, но часто значительно быстрее. То, что вы думаете, что это не изменится, не означает, что это не изменится.
Майкл Андерсон

Ответы:

78

Регрессионное тестирование

Это все о регрессионном тестировании .

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

Отвлекаясь, он инвертирует две постоянные.

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

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

Хранение оригинальных тестов, которые вы написали, может предотвратить такую ​​боль. Отвлеченный разработчик фиксирует код и почти сразу видит, что он что-то сломал; такой код даже не достигнет производства. Модульные тесты, кроме того, будут очень точно определять местонахождение ошибки . Решить это не составит труда.

Побочный эффект ...

На самом деле, большинство рефакторинга в значительной степени основано на регрессионном тестировании. Сделайте небольшое изменение. Тест. Если это пройдет, все в порядке.

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

Имея полный набор тестов, вы поощряете рефакторинг, а значит, и более чистый код. Без риска, становится очень заманчивым проводить рефакторинг на регулярной основе.

Изменения в требованиях

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

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

Арсений Мурзенко
источник
Педантическое примечание: ничто не без риска. ;)
jpmc26
2
Регрессия - причина номер один для тестов для меня - даже если ваш код четко определяет обязанности и использует только то, что ему передано (например, не глобальные), слишком легко что-то сломать случайно. Тесты не идеальны, но они все еще хороши (и почти всегда предотвращают повторное появление одной и той же ошибки, о чем большинство клиентов недовольны). Если ваши тесты работают, нет затрат на обслуживание. Если они перестают работать, вы, вероятно, нашли ошибку. Я работаю над унаследованным приложением с тысячами глобальных объектов - без тестов я бы не осмелился внести многие необходимые изменения.
Луаан
Еще одно педантичное примечание: некоторые «магические» числа на самом деле в порядке, и замена их константами - плохая идея.
Дедупликатор
3
@Deduplicator, мы это знаем, но многие из младших программистов, прошедших специальную подготовку в университете, крайне ревностно относятся к слепому следованию мантрам. И "магические числа являются злом" является одним из них. Другой - «все, что используется более одного раза, должно быть преобразовано в метод», что в крайних случаях приводит к множеству методов, содержащих только одно утверждение (а затем они задаются вопросом, почему производительность внезапно упала, они никогда не узнали о вызовах).
jwenting
@jwenting: только что добавил это примечание, потому что MainMa написала «нет ничего плохого в этом изменении».
Дедупликатор
45

Потому что ничто не так просто, чтобы не было ошибок.

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

Кроме этого есть ошибка ...

public function polynominal($a, $b, $c, $d)
{
    return  $a * pow($x, 3) + $b * pow($x, 2) + $c * $x + $d;
}

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


Приложение:

Хотя вы можете пока считать, что ваш код не содержит ошибок, трудно сказать, как долго это будет продолжаться. Хотя можно утверждать, что написание теста для такого тривиального фрагмента кода не стоит, но если вы уже написали тест, работа сделана, а удаление его означает удаление ощутимого безопасного средства защиты.

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

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


источник
Есть альтернативное решение: переключение на не динамические и не слабые языки. Модульные тесты - это обходной путь для недостаточно сильной проверки во время компиляции, и некоторые языки хороши в этом en.wikipedia.org/wiki/Agda_(programming_language)
День
21

Да. Если бы мы могли сказать с уверенностью 100%, с уверенностью: эта функция никогда не будет редактироваться и никогда не будет выполняться в контексте, который может вызвать ее сбой - если бы мы могли сказать это, мы могли бы отказаться от тестов и сэкономить несколько миллисекунд на каждом CI build.

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

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

Карл Манастер
источник
Я думаю, что это очень хорошая мысль, которую вы здесь обсуждаете. Я уже вижу двух парней, которые часами спорят о том, чтобы держать тесты на небольшую функциональность.
Капол
@Kapol: не аргумент, встреча, чтобы определить, что может пойти, а что остаться, и какие критерии следует использовать для принятия решения, а также где документировать критерии и кто подписывает окончательное решение ...
jmoreno
12

Все сказанное в других ответах верно, но я добавлю еще один.

Документация

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

Это может облегчить обнаружение ошибки и уменьшить путаницу.

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

Пример того, насколько он может быть полезен в качестве документации, приведен во введении «Жасмин»: http://jasmine.github.io/edge/introduction.html Дайте ему несколько секунд для загрузки, затем прокрутите вниз. Вы увидите весь Jasmine API, документированный как результат модульного теста.

[Обновление на основе отзывов @Warbo] Тесты также гарантированно актуальны, так как в противном случае они не пройдут, что, как правило, приведет к сбою сборки, если используется CI. Внешняя документация изменяется независимо от кода и, следовательно, не обязательно обновляется.

Brandon
источник
1
Существует большое преимущество использования тестов в качестве (одной части) документации, которую вы оставили неявной: они автоматически проверяются. Другие формы документации, например. Пояснительные комментарии или фрагменты кода на веб-странице могут устареть по мере развития кода. Модульные тесты вызовут сбой, если они более не точны. Эта страница с жасмином является ярким примером этого.
Warbo
Я хотел бы добавить, что некоторые языки, такие как D и Rust, интегрируют генерацию документации с модульным тестированием, так что вы можете иметь один и тот же кусок кода, который компилируется в модульный тест и вставляется в документацию HTML
Idan Arye
2

Проверка на практике

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

У вас есть бюджет. Ваша задача состоит в том, чтобы получить лучший продукт, который вы можете с таким бюджетом, для любого определения «лучшего», которое вы можете собрать (это не простое определение). Конец истории.

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

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

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

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

Да, продолжайте тесты, поддерживайте их работу и поддерживайте их прохождение.

Модульные тесты предназначены для защиты вас (и других) от вас самих (и самих себя).

Почему держать тесты хорошей идеей;

  • Проверка функциональности предыдущих требований перед лицом новых требований и дополнительных функциональных возможностей.
  • Убедитесь, что упражнения по рефакторингу верны
  • Внутренняя документация - это то, как код будет использоваться
  • Регрессионное тестирование, все меняется
    • Изменения нарушают старый код?
    • Требуют ли изменения дальнейших запросов на изменение или обновлений текущих функций или кода?
  • Учитывая, что тесты уже написаны, сохраните их; уже потраченные время и деньги позволят сократить расходы на техническое обслуживание в дальнейшем
Найл
источник
2
Это, кажется, не предлагает ничего существенного по сравнению с другими ответами, которые были предоставлены.
1
Ранее была проблема с размещением сообщения, но в ретроспективе это хорошее резюме. Я удалю это.
Найл
-1

Документация для разработчиков

  • Как мне (как другому разработчику) узнать, что это было проверено?
  • Если я хочу исправить ошибку в автономной функции, как мне узнать, что я не представляю ошибку, которую вы уже рассмотрели?
  • Индикатор сложности: количество тестов может быть хорошим показателем сложности чего-либо. Это может указывать на то, что вам не следует прикасаться к нему, потому что он зрелый и стабильный или что он раздутый и спаренный.

Документация пользователя

  • В ожидании плохого документа, я могу посмотреть, приняты ли {ноль, отрицательные значения, пустые наборы и т. Д.} И каково ожидаемое возвращаемое значение.
  • Это также дает хороший пример того, как я должен использовать объект / функцию
sixtyfootersdude
источник
1
кажется, что это не дает ничего существенного по поводу высказанных и объясненных в предыдущих ответах вопросов Даже «хорошее резюме» уже опубликовано (на мой вкус, это не так хорошо, но да ладно)
комнат