Подтвердить что ты не робот

Каковы недостатки, использующие случайные значения в модульном тестировании?

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

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

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

Есть минусы?

4b9b3361

Ответ 1

Downsides

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

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

В-третьих, если имеется широкий диапазон входов и выходов, вы можете часто покрывать тот же диапазон, используя хорошо выбранные значения в стандартных модульных тестах, с меньшей сложностью. Например. выберите числа -max, (-max + 1), -2, -1, 0, 1, 2, max-1, max. (или что-то интересное для алгоритма).

расквитаться

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

Дополнительные трюки

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

Ответ 2

Они случайные.

(Ваш тест может работать случайным образом, даже если ваш код не работает.)

Ответ 3

Кроме того, вы не сможете повторять тесты много раз. A unit test должен работать точно так же с заданными параметрами.

Ответ 4

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

Ответ 5

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

Ответ 6

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

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

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

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

Ответ 7

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

Если у вас уже есть представление о диапазоне значений, которые вы хотите протестировать, вы должны либо (1) создать другой тест для каждого значения в диапазоне, либо (2) цикл над набором значений и сделать утверждение на каждой итерации. Быстрый, довольно глупый пример...

for($i = 0; $i < 10; $i++)
  $this->assertEquals($i + 1, Math::addOne($i));

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

Ответ 8

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

Пример.

Username= "username".rand();
Save_in_DB("user",Username); // To save it in DB
Verify_if_Saved("user",Username); 

Ответ 9

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

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

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

Ответ 10

Upsides: они показывают, когда ваши другие тесты не охватили все инварианты. Если вы хотите, чтобы ваш CI-сервер запускал недетерминированные тесты, это еще одна проблема. Учитывая, насколько невероятно полезен, я нашел https://www.artima.com/shop/scalacheck, я не собираюсь обходиться без него с этого момента. Скажем, вы реализуете алгоритм сопоставления шаблонов. Вы действительно знаете все разные угловые случаи? Я не. Рандомизированные входы могут вывести их из строя.

Ответ 11

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

Отладка это правильная боль в задней части, а иногда (рядом) невозможно.

Кроме того, часто трудно сказать, что на самом деле тестирует ваш тест (и тестирует ли он вообще что-либо).

Исторически в моей компании мы использовали случайные тесты на нескольких уровнях (Unit, Integration, SingleService Tests), и это изначально казалось отличной идеей - это экономит ваш код, пространство и время, позволяя тестировать несколько сценариев в одном тесте.

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