Случайные данные в модульных тестах?

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

Я дал ему несколько разных причин против этого, основными из которых являются:

  • случайные значения означают, что тест не является действительно повторяемым (что также означает, что если тест может случайно потерпеть неудачу, он может сделать это на сервере сборки и сломать сборку)
  • если это случайное значение и тест не выполняется, нам необходимо: a) исправить объект и b) заставить нас каждый раз проверять это значение, поэтому мы знаем, что он работает, но поскольку он случайный, мы не знаем, какое значение было

Другой сотрудник добавил:

  • Если я тестирую исключение, случайные значения не гарантируют, что тест заканчивается в ожидаемом состоянии
  • случайные данные используются для промывки системы и нагрузочного тестирования, а не для модульных испытаний

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

(Или, альтернативно, это приемлемый метод написания модульных тестов, и я и мой другой коллега ошибаются?)

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

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

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

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

Java http://functionaljava.org/

Scala (или Java) http://github.com/rickynils/scalacheck

Haskell http://www.cs.chalmers.se/~rjmh/QuickCheck/

.NET: http://blogs.msdn.com/dsyme/archive/2008/08/09/fscheck-0-2.aspx

Эти инструменты будут использовать ваши хорошо сформированные спецификации в качестве входных данных и автоматически генерировать столько модульных тестов, сколько вы хотите, с автоматически генерируемыми данными. Они используют «сокращающиеся» страtagsи (которые вы можете настроить), чтобы найти простейший возможный тестовый случай, чтобы сломать ваш код и убедиться, что он хорошо закрывает края.

Счастливое тестирование!

Этот тип тестирования называется тестом Monkey . Когда все сделано правильно, оно может выкуривать ошибки из действительно темных углов.

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

Здесь есть полупустынный дом, который имеет какое-то применение, а именно для того, чтобы выровнять ваш PRNG с постоянной. Это позволяет генерировать «случайные» данные, которые повторяемы.

Лично я думаю, что есть места, где (постоянные) случайные данные полезны при тестировании – после того, как вы думаете, что сделали все ваши тщательно продуманные углы, использование стимулов от PRNG иногда может найти другие вещи.

В книге « Красивый код» есть глава под названием «Красивые тесты», где он проходит страtagsю тестирования для алгоритма двоичного поиска . Один абзац называется «Random Acts of Testing», в котором он создает случайные массивы для тщательного тестирования алгоритма. Вы можете прочитать некоторые из этих онлайн-книг в Google Книгах, страница 95 , но это отличная книга, которая стоит того.

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

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

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

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

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

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

  • если это случайное значение и тест не выполняется, нам необходимо: a) исправить объект и b) заставить нас каждый раз проверять это значение, поэтому мы знаем, что он работает, но поскольку он случайный, мы не знаем, какое значение было

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

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

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

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

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

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

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

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

Существует много чего сказать о рандомизированных тестах, особенно хорошо написанных, поэтому не спешите их увольнять!

Можете ли вы генерировать некоторые случайные данные один раз (я имею в виду ровно один раз, а не один раз на тестовый прогон), а затем использовать его во всех тестах после этого?

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

Вы должны спросить себя, какова цель вашего теста.
Модульные тесты состоят в проверке логики, streamа кода и взаимодействий объектов. Использование случайных значений пытается достичь другой цели, тем самым уменьшая целевую аудиторию и простоту. Это приемлемо для удобства чтения (генерирование UUID, идентификаторов, ключей и т. Д.).
В частности, для модульных тестов я не могу вспомнить даже после того, как этот метод успешно нашел проблемы. Но я видел множество проблем детерминизма (в тестах), пытающихся быть умными со случайными значениями и в основном со случайными датами.
Тестирование Fuzz – это действительный подход для интеграционных тестов и сквозных тестов .

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

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

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

Мы просто столкнулись с этим сегодня. Я хотел псевдослучайный (так что это выглядело бы как сжатые аудиоданные с точки зрения размера). Я сказал, что также хотел детерминировать . Ранг () отличался от OSX, чем от Linux. И если я не заново сеял, он может измениться в любое время. Таким образом, мы изменили его на детерминированный, но все же psuedo-random: тест повторяется, так же как и использование консервированных данных (но более удобно написанное).

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

Означает ли это, что это все равно, является случайным? Давайте поговорим о пиве. 🙂

Я могу предусмотреть три решения проблемы тестовых данных:

  • Тест с фиксированными данными
  • Тестирование со случайными данными
  • Генерируйте случайные данные один раз , затем используйте его как фиксированные данные

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

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

Как может ваш парень снова запустить тест, когда он не смог проверить, исправил ли он его? Т.е. он теряет повторяемость тестов.

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

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

Interesting Posts

Polymer querySelector работает с DartVM, но не в Chrome после компиляции

Должен ли я использовать «Spread Spectrum Clocking» на моем новом диске Western Digital?

Взгляд и чувство не обновляется в Swing JTabbedPane

Как я могу взаимодействовать с элементами за прозрачным Android-приложением?

Как вернуть данные JSON из весеннего controllerа с помощью @ResponseBody

Обработка строк с помощью & или + в VB.NET

автоматическая прокрутка TextView в android для отображения текста

Как изменить время ожидания тайм-аута запроса nodejs?

любой инструмент для java-объекта для сопоставления объектов?

Рекомендации по переходу на расширение Chrome

Регулярное выражение для asp: RegularExpressionValidator с форматом MMddyy (выпуск високосного года)

Отображение 2 векторов – помощь в векторизации

В чем разница между этими функциями jQuery?

Развернуть диапазоны, определенные столбцами «от» и «до»

Как подключить ПК с Windows 8 к домену Samba

Давайте будем гением компьютера.