Тестирование со случайными входами лучшие практики - PullRequest
9 голосов
/ 01 ноября 2008

ПРИМЕЧАНИЕ : я упоминаю следующую пару абзацев в качестве фона. Если вы просто хотите TL; DR, не стесняйтесь переходить к пронумерованным вопросам, поскольку они только косвенно связаны с этой информацией.

В настоящее время я пишу скрипт на python, который делает некоторые вещи с датами POSIX (среди прочего). Тем не менее, модульное тестирование кажется немного сложным, поскольку существует такой широкий диапазон дат и времени.

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

Итак, вот несколько вопросов (в основном косвенно связанных с вышеизложенным):

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

Похожие темы:

Ответы [ 9 ]

12 голосов
/ 01 ноября 2008

Я согласен с Федерико - рандомизированное тестирование контрпродуктивно. Если тест не будет надежно пройден или не пройден, его очень трудно исправить и знать, что он исправлен. (Это также проблема, когда вы вводите ненадежную зависимость, конечно.)

Однако вместо этого вы можете убедиться, что у вас есть хорошее покрытие данных другими способами. Например:

  • Убедитесь, что у вас есть тесты для начала, середины и конца каждого месяца каждого года между 1900 и 2100 (если, конечно, они подходят для вашего кода).
  • Используйте различные культуры или "все из них", если это известно.
  • Попробуйте "день 0" и "один день после окончания каждого месяца" и т. Д.

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

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

РЕДАКТИРОВАТЬ: Я уверен, что есть мест, где полезны случайные тесты, хотя, вероятно, не для модульных тестов. Однако в этом случае я хотел бы предложить кое-что: использовать один RNG для создания случайного, но известного начального числа, а затем запустить новый RNG с этим значением - и зарегистрировать его. Таким образом, если произойдет что-то интересное, вы сможете воспроизвести его, запустив ГСЧ с зарегистрированным начальным числом.

6 голосов
/ 01 ноября 2008

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

3 голосов
/ 01 ноября 2008

Ух ты, отличный вопрос! Некоторые мысли:

  • Случайное тестирование - это всегда хорошая работа по укреплению доверия, хотя, как вы упомянули, лучше всего подходит для определенных типов кода.
  • Это отличный способ стресс-тестирования любого кода, производительность которого может быть связана с количеством выполнений или последовательностью входных данных.
  • Для довольно простого кода или кода, который ожидает ограниченного типа ввода, я бы предпочел систематический тест, который явно охватывает все вероятные случаи, выборки каждого маловероятного или патологического случая и все граничные условия.
1 голос
/ 28 апреля 2009

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

  • Хорошие / плохие кандидаты? Рандомизированные тесты хороши для выявления крайних случаев (исключений). Проблема состоит в том, чтобы определить правильный результат рандомизированного ввода.
  • Определение количества раз выполнения кода: просто попробуйте его, если это занимает слишком много времени, уменьшите количество итераций Возможно, вы захотите использовать инструмент покрытия кода, чтобы узнать, какая часть вашего приложения на самом деле тестируется.
  • Подходят ли эти виды тестов для юнит-тестов? Да.
1 голос
/ 06 ноября 2008

Несколько вещей:

  • При случайном тестировании вы не можете точно сказать, насколько хорош фрагмент кода, но вы можете сказать, насколько плохо это.
  • Случайное тестирование лучше подходит для вещей, которые имеют случайные входные данные - ярким примером является то, что открыто для пользователей. Так, например, то, что случайно щелкает и печатает по всему вашему приложению (или ОС), является хорошим тестом общей надежности.
  • Аналогичным образом, разработчики считаются пользователями. Так что то, что случайным образом собирает GUI из вашей среды, является еще одним хорошим кандидатом.
  • Опять же, вы не найдете всех ошибок таким образом - что вы ищете, так это "если я сделаю миллион сумасшедших вещей, не приведет ли ЛЮБОЕ из них к повреждению системы?" Если нет, то вы можете почувствовать некоторую степень уверенности в том, что ваше приложение / OS / SDK / что угодно может выдержать воздействие нескольких дней для пользователей.
  • ... Но, что более важно, если ваше тестовое приложение с произвольным верхом может вызвать сбой вашего приложения / OS / SDK примерно за 5 минут, это примерно столько времени, сколько у вас будет до первой пожарной тренировки, если вы попытаетесь отправить эту присоску.

Также обратите внимание: ВОСПРОИЗВОДИТЕЛЬНОСТЬ ВАЖНА В ТЕСТИРОВАНИИ! Следовательно, пусть ваш тестовый инструмент регистрирует случайное начальное число, которое он использовал, и имеет параметр для запуска с того же самого начального числа. Кроме того, он должен начинаться либо с известного «базового состояния» (, т. Е. , переустановить все с образа на сервере и начать с него), либо с некоторого восстанавливаемого базового состояния (, т. Е. , переустановите из этого образа, а затем измените его в соответствии с произвольным начальным числом, которое тестовый инструмент принимает в качестве параметра.)

Конечно, разработчики будут благодарны, если в инструменте есть такие приятные вещи, как «сохранять состояние каждые 20 000 событий» и «останавливаться прямо перед событием №» и «шаг вперед 1/10/100 событий». Это очень поможет им воспроизвести проблему, найти и исправить ее.

Как заметил кто-то другой, серверы - это еще одна вещь, которая предоставляется пользователям. Получите список из 1 000 000 URL-адресов (grep из журналов сервера), а затем отправьте их генератору случайных чисел.

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

Ах, да, еще одна вещь: в дополнение к тестам "толкни его так быстро и тяжело, как ты можешь", у тебя есть возможность делать "точно то, что реальный пользователь [который, возможно, был ненормальный, или ребенок, привязывающий клавиатуру / мышь] будет делать. " То есть, если вы делаете случайные пользовательские события; выполняйте их со скоростью, которую может сделать очень быстрая машинистка или очень быстрый пользователь мыши (со случайными задержками, чтобы симулировать МЕДЛЕННОГО человека), в дополнение к «так быстро, как моя программа может выплевывать события». Это два ** очень разных * типа тестов, которые при обнаружении ошибок будут вызывать очень разные реакции.

1 голос
/ 02 ноября 2008

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

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

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

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

0 голосов
/ 28 октября 2017

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

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

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

0 голосов
/ 28 апреля 2009

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

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

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

Затем вы переходите от случайного тестирования к статистическое тестирование.

if (a > 0)
    // Do Foo
else (if b < 0)
    // Do Bar
else
    // Do Foobar

Если вы выберете a и b в случайном порядке int диапазон, вы тренируетесь Foo 50% время Bar 25% времени и Foobar 25% времени. Скорее всего что вы найдете больше ошибок в Foo чем в Bar или Foobar.

Если вы выберете a таким, что это отрицательный 66,66% времени, Bar и Foobar тренируйся больше, чем с ваше первое распространение. Действительно три ветви выполняются каждый 33,33% времени.

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

0 голосов
/ 28 апреля 2009

Это может быть немного не по теме, но если вы используете .net, есть Pex , который делает нечто похожее на рандомизированное тестирование, но с большей интуицией, пытаясь сгенерировать «случайный» контрольный пример, который использует все пути в вашем коде.

...