Pull to refresh

Comments 54

Мы решили сократить выборку до первых 100 фирм, подходящих под наши условия
Зная примерное количество и, предположив, что id — int, можно сделать так:
$id = random_int(1, 150000);
…where id < :id limit 1
По вашему алгоритму, скажем выпало $id=100, мы берем эти 100 фирм и из них может ни одна не подходить для наших условий, например, необходимо наличие телефона. Также это не работает и в обратном случае, если сначала выбрать фирмы с телефоном, а потом сравнивать с $id=100.
Верно. В таком случае, можно заморочиться и сделать сначала получение min и max id из результатов и вторым запросом получить реальные данные, используя случайный id из (mix, max)
Да, только по сути это тот же подход, только другая реализация.
На сколько понял — самое слабое место, с точки зрения производительности, это получение тестовых данных из БД. Так может вообще отказаться от этого? Тем более рандомом нельзя гарантировать проверку всех кейсов. Лучше написать качественные дата-провайдеры, которые будут генерить необходимые данные для всех кейсов.
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
В Linux можно монтировать папку в память, ну вот там где у нас PostgreSQL и монтируем.
UFO just landed and posted this here
Стоит так же наверное отметить что и в Windows такой функционал вполне реализуем с помощью RamDisk (если что решение стороннее, не от Microsoft)
Мы тоже так думаем, что лучше написать такие дата-провайдеры. Даже проводили пару испытаний. Эта задача оказалась гораздо сложнее чем кажется, потому что собрать объект, у которого есть несколько зависимостей необходимых для работы приложения не простая задача. Мы не отказываемся от их написания в будущем, просто быстрее оказалось сделать выше описанные изменения и получить огромный профит.

Вот насчёт регулярной очистки БД. Приложение с маленьким количеством данных будет вести себя не так, как приложение с большим. По идее нужно отдельное тестирование для проверки как приложение ведёт себя на нормальном объёме данных. Делаете вы такое?

Если вы про нагрузочное тестирование, то да, такой вид тестирования у нас есть. Оно поставлено у нас на поток и при каждом релизе сравниваем нагрузку текущего кода с его прошлой версией.
«Почему не поймали описанную проблему выше на этапе нагрузочного тестирования» — спросите Вы. Не поймали мы ее из-за особенностей нагрузочного контура — БД там синкатеся по другому.
В большинстве случае различия в поведении на разных объёмах данных покрываются не функциональными тестами. Обычно нет в коде условий вроде «если в таблице больше 100500 записей, то вести себя по другому».
Обычно нет в коде условий вроде «если в таблице больше 100500 записей, то вести себя по другому

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

Строго говоря, это вообще не относится к тестированию приложения (если бизнес-логика не размазана и на слой хранимок). Тут тестирование БД. А ее и не нужно тестировать — просто ищешь результаты таких тестов и помнишь про ограничения.
Строго говоря, это вообще не относится к тестированию приложения

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


А ее и не нужно тестировать — просто ищешь результаты таких тестов и помнишь про ограничения.

Не совсем понял. Тут же приложение в связке с БД тестируется.

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

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

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

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


Потом оно, конечно, выводит сообщение юзеру, но вообще транзакции должны отрабатывать за 3 секунды или быстрее. Над этим надо работать. И чем больше данных, тем дольше работают транзакции. Поэтому проверять надо на БД с большим количеством записей.

Аккуратней со словом «падать» в отношении приложения. Думаю большинство его тут воспринимает куда катастрофичней :)

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

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


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

Может они и не являются нагрузочными в узком смысле слова, но точно они относятся к нефункциональным, если от количества данных результат меняться не должен качественно. Другое дело, что если опытным путём установлено или в документации прочитано, что на 1 млрд записей база падает, а это не допустимо, то делается функциональность блокирующая добавление записи при наличии 999999999 и вот это уже тестом должгл покрываться. Или вводится ограничение, что если запрос больше 30 секунд длится, то он должен рубиться, а юзеру внятное сообщение.
А не пробовали создать докер-образы БД, куда просто копировать раз в день минимально-необходимый набор данных для тестов? Так же решается проблема с удалением тестовых данных — после прогона тестов образ просто удаляется до следующей загрузки.
Все это прекрасно работает через teamcity, к примеру.
Нет, мы так не пробовали. Тут не понятен механизм определения минимального набора данных. Можно конечно, ночью прогонять тесты на полном объеме данных, получать id объектов из базы, сохранять их и потом копировать эти объекты в отдельную базу, чтобы остальные прогоны этого дня шли быстро. Мы обязательно подумаем над этим способом более пристально. Спасибо, за совет!

Рандом в тестах почти всегда зло. Хотя бы потому что повторить такой тест невозможно (если у вас нет ключика, который фиксирует состояние… но тогда это другой тест).
Покапитаню: Классы эквивалентности пробовали применять?
Покапитаню2: С выборкой "случайной" фирмы вы тестировали несуществующий кейз. Клиент может запросить "случайную фирму" (а-ля "фирма дня"?) Если нет, то и тесты (массово) такой подход не должны использовать.

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

Рандом как раз в тестах, просто во входных данных. Получается, что чистая функция "тест" обёрнута в обычную функцию "тест + входные данные" + рандомное время для извлечения тестовых данных.
Конечно, всегда есть некоторая рандомизация (да хотя бы время, в которое скрипт запустили — вдруг фирма уже прекратила существование и уже отправилась в архив), но и от неё надо стараться избавиться.
А создание ненастоящих фирм можно делать отдельным процессом, который будет контролировать наполненность тестовой фермы тестовыми сущностями. Так нагрузка будет не на тесты.

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

Ещё один недостаток: ошибка может быть в генерации тестовых данных.


Я не защищаю такой подход, но он вполне может существовать на временной основе, пока не сделают генерилку данных. В нем нет каких-то фундаментально-неверных подходов.

Я согласен с вами, что такой подход рабочий. Но нет ничего более постоянного, чем что-то временное :)
Некоторая ошибочность подхода (не фатальная) в том, что вы тестируете гораздо больше, чем вам надо. Просто "а почему бы и нет?" Раз вас пока это устраивает — исползуйте на здоровье :)

+1 (не могу добавить)
Зачем играть в рулетку с рандомом на настоящей базе, когда требования к данным для теста (граничные случаи) известны? Какое-то усложнение во всём: медленнее; нет гарантии, что тест проходит со смыслом (попали в требуемые по форме данные); не повторить разработчику для отладки, если будет нужно.
Чуть выше я отвечал, что мы тоже думаем о предзаполняемых данных перед тестом, но это очень трудоемкая задача.
В целом она трудоёмкая для уже существующей системы, полную историю которой помнят только репозиторий да продакшен база. Когда с нуля или почти с нуля начинаешь, или хотя бы есть большая вероятность, что изменения в базе необходимые для работы приложения (типа системных справочников) делались известными миграциями, то всё гораздо проще.

https://www.phparch.com/magazine/2018-2/april/


PHPUnit Worst Practices — моя статья (наглая реклама, да!). Очень знакомо выглядит то, что вы описываете, несмотря на то, что моя статья больше о качестве тестового кода, чем о производительности тестов.


И отдельно насчет рандома. Рандом в тестах — зло. Тест должен быть предсказуемым, а когда используется рандом — вы рискуете получить рандомные же фэйлы. Покрывайте edge cases, а случайные данные оставьте для fuzzing tests.

Рандом в тестах — зло.

И генерация рандомных идентификаторов тоже?

Если используете один и тот же seed или можете восстановить цепочку, то не зло.

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

Ну например, генератор использует 100 возможных символов для этой строки, а в валидатор зашито только 99. Один из Х тестов падает, повторный запуск — скорее всего проходит.

Ну например, генератор использует 100 возможных символов для этой строки, а в валидатор зашито только 99.

В логах же можно будет посмотреть, почему упал тест? И по результатам поправить или валидатор, или генератор.

Ваш случай граничный. Сложно придумать контраргумент. Возможно, даже и не нужно, ведь есть "допустимое зло" :)
Из тех условий, что вы приводите, рандомная генерация ключей единственное что не позволит воспроизвести какую-нибудь ошибку в БД или хранимых процедурах в БД.
Но это уже домыслы.

Два совпадения рандомных чисел.

На практике невероятно.

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

Очень сложно выглядит.

Передача сгенерированного значения, вместо передачи подготовленного значения что-то усложняет?


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

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

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


Чистилка не особо нужна, если каждый раз использовать эталонную базу (восстанавливать из докера / хранить слепок / всё что угодно).


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

> Если что-то подготавливать, то после теста надо будет почистить всё, что попало в БД.

В целом это общее правило для любых тестов. Влияние тестов друг на друга должно быть стремиться к нулю. Если куча всего пишется в базу во время тестов и это не чистится, то о независимости прогонов друг от друга сложно говорить.
Первые 2 — «Голый пистолет 3». Последняя — «Голый пистолет»
Сколько специалистов создают и поддерживают 25К тестов?
Сейчас тестировщиков на проекте пять. В основном они и создают и поддерживают, но разработка так или иначе тоже вносит свой ощутимый вклад в эти процессы.
Sign up to leave a comment.