Каковы недостатки, использующие случайные значения в модульном тестировании?
Я говорю о крупномасштабной системе с большим количеством серверов и недетерминированным входом в высокой емкости. Когда я говорю "недетерминированный", я говорю о сообщениях, которые отправляются, и вы понимаете, что можете, и делаете все, что можете. Существует много типов сообщений, поэтому ввод может быть очень сложным. Я не могу представить, как написать код для такого большого количества сценариев, и простой неслучайный (детерминированный) генератор сообщений недостаточно хорош.
Вот почему я хочу провести рандомизированный юнит-тест или тестирование сервера, чтобы в случае сбоя можно было записать журнал.
И я предпочитаю юнит-тест вместо случайного инжектора, потому что я хочу, чтобы он работал как часть автоматических тестов ночной сборки.
Есть минусы?
Ответы
Ответ 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), и это изначально казалось отличной идеей - это экономит ваш код, пространство и время, позволяя тестировать несколько сценариев в одном тесте.
Но все чаще это становится критическим моментом в нашем развитии, когда наши (даже исторические и надежные в прошлом) тесты начинают проваливаться случайным образом, и их исправление является трудоемким.