Pull to refresh

Comments 16

Расскажите лучше, как вы тестируете скорость модулей, когда она напрямую зависит от скорости работы других модулей.
Например, модуль A 15 раз вызывает модуль B, который делает запросы в БД, которые могут тормозить. Получается ведь, чтобы протестировать скорость работы модуля A, нужно не только не подделывать модуль B и БД, но и иметь в тестовой БД что-то отдалённо напоминающее реальную базу.

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

Или может я не того хочу?
У нас тесты отдельно, бенчмарки отдельно. В статье речь только про тесты. В бенчмарках же мы используем Devel::NYTProf, он может показывать inclusive time/exclusive time для
выполняемого кода. В общем, ничего подделывать никому не понадобилось пока, мы, наверное, не настолько сильно увлечены оптимизацией.
У нас скорость работы основных узлов сайта с точки зрения пользователя меряется функциональными тестами. А целью покрыть всю систему «юнит-бенчмарками» мы никогда не задавались. Это вряд ли реально, да и непонятно, зачем нужно. Самые тормозные места у нас в 99,9% случаев — запросы к БД либо сетевые запросы к внешним сервисам. Первое — проверяется в момент разработки. Со вторым, во-первых, всё равно мало что можно сделать, во-вторых, по большей части оно вынесено во всякие асинхронные очереди и т. д., чтобы минимально напрягать пользователя.
Я не про юнит-тестирование сейчас. Понятно, что бенчмарки тестируют систему в целом. Я про инфраструктуру таких бенчмарков, а точнее про тестовые данные к ним.
Как фиксятся тормозные места понятно, речь о том, как они автоматизированно проверяются.
К сожалению, как показывает практика, автоматический инструмент для поиска тормозных мест способен, обычно, только указать направление, в котором стоит копать далее. В любом случае, выхлоп такого инструмента придется анализировать руками и проверять, а есть ли проблема. Да и не все тормозные места можно вообще бенчмарками обнаружить. Точнее не так. Обнаружить факт наличия тормозов можно, а вот их местоположение – далеко не факт.
Ну вот у нас автоматизированно проверяется производительность только функциональными тестами. Тесты эти работают на сервере с копией боевой БД (а некоторые даже на продакшне прогоняются). Так что специально данные для них мы не готовим, а работаем на реальных (или их копии).

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

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

А если функционал активно меняет данные? После каждого теста восстанавливать всю базу в исходное состояние? Это явно долго.
Серебрянной пули тут не будет. Можно, например, тесты внутри транзакции прогонять и откатывать её после завершения. Можно вручную чистить/восстанавливать именно конкретные изменённые данные. Можно полностью восстанавливать БД. Всё зависит от объёма данных, от частоты прогона тестов, от того, что они делают, и т. п.

А самый главный вопрос — как результат выполнения теста влияет на последующие тесты (и влияет ли вообще как-то)? Функциональные тесты в большинстве случаев не делают ничего такого, чтобы друг другу помешать. Консистентность базы они не ломают, количество данных не меняют заметно. Так что восстанавливать если и есть необходимость, то только после прогона всей порции тестов.

И, естественно, на искуственном наборе данных у вас будут ровно такие же сложности.

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

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

А самый главный вопрос — как результат выполнения теста влияет на последующие тесты (и влияет ли вообще как-то)?

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

В целом я вас услышал, спасибо.
Подскажите, а как вы организуете запуск и хранение тестов на диске? Набор .t-файлов? Test::Class? Что-то еще?
Такие же соглашения как у CPAN модулей — тесты это .t файлы находящиеся в t/ директории.
При этом для модуля lib/AAA/BBB/CCC.pm тест находится в t/AAA/BBB/CCC.t или есть несколько тестов t/AAA/BBB/CCC_xxx.t
При этом в одной из директорий в lib/ (вне t/) находится общий код для тестов (библиотеки для тестирования)

Механизмом Test::Spec для общего кода habrahabr.ru/company/regru/blog/239417/#shared_code сейчас стараемся не пользоваться.
Спасибо. Если можно, еще уточню: а тесты — это классы? Приходилось делать абстрактные базовые классы тестов? Если да, то как?
Нет, не классы. Тесты либо на базе Test::More либо на базе Test::Spec, ни там ни там не нужно базовых классов и их не делаем.
Запуск через prove --jobs X

на самом деле структура более сложная — тесты в Jenkins прогоняются скриптом, который отбирает стабильные тесты и парсит TAP-выхлоп удобным образом.
Статья просто супер, побольше бы таких!
Кстати, выложил версию, которая позволяет локализовывать значения (local) через around/yield.

пример спеки.

пока автор не принял изменения, можно ставить через cpanm с github
Sign up to leave a comment.