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

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

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

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

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
1
1 377
11
Перейти к ответу Данный вопрос помечен как решенный

Ответы 11

Что ж, учитывая, что они случайны, на самом деле нет способа убедиться, но проверка 100000 паролей должна развеять большинство сомнений :)

Ответ принят как подходящий

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

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

Если вы не можете контролировать начальное число и нет способа предотвратить рандомизацию функции, которую вы тестируете, то, думаю, вы застряли в непредсказуемом модульном тесте. :(

@Parappa: вы всегда можете запускать случайный код достаточно раз, чтобы быть уверенным, что вы покрыли все свои базы с помощью некоторой альфы.

user7116 18.09.2008 02:06

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

Kris Kumler 18.09.2008 02:36

Вы также можете изучить тестирование мутаций (Шут для Java, Heckle для Ruby)

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

Paweł Hajdan 18.09.2008 14:40

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

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

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

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

Есть ли что-нибудь в кодовой базе, которое проверяет сгенерированные пароли, чтобы убедиться, что они достаточно случайны? Если нет, я могу посмотреть на создание логики для проверки сгенерированных паролей, ее тестирования, а затем вы можете заявить, что генератор случайных паролей работает (поскольку «плохие» пароли не выйдут).

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

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

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

Не зная, каковы ваши правила, трудно сказать наверняка, но если предположить, что они похожи на «пароль должен состоять не менее чем из 8 символов с как минимум одной заглавной буквой, одной строчной буквой, одной цифрой и одним специальным символом», тогда это невозможно даже с помощью грубой силы проверить достаточное количество сгенерированных паролей, чтобы доказать правильность алгоритма (так как это потребовало бы где-то более 8 ^ 70 = 1,63x10 ^ 63 проверок в зависимости от того, сколько специальных символов вы назначили для использования, что потребовало бы очень , очень долго доделывать).

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

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

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

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

Либо используйте фиксированное случайное начальное число, либо сделайте его воспроизводимым (т. Е .: производным от текущего дня)

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

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

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

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

Это не гарантирует, что наша функция ведет себя так, как задумано (если мы передадим равное количество красных и синих шариков и будем иметь равные веса, всегда ли мы получим распределение 50/50 по большому количеству испытаний?), Но на практике это так. часто достаточно.

Другие вопросы по теме