Я перенес свои сообщения в собственный блог, потому что Medium становится все менее и менее удобным для читателей (платный доступ, невозможность выделить код и т. Д.). Чтобы прочитать эту статью в более приятном и дружественном контексте, прочтите ее в моем личном блоге и подписывайтесь на меня в Twitter, чтобы получать уведомления!
Https://titouangalopin.com/tips-for-a-reliable-and-fast-test-suite-with-symfony-and-doctrine/
На мой взгляд, одна из величайших особенностей Symfony - это внутренняя организация вокруг HTTP: его ядро обрабатывает HTTP-запросы и возвращает HTTP-ответы (см. Документацию по этому поводу). Этот шаблон является одним из основных принципов фреймворка, и он дает массу преимуществ.
Одним из этих преимуществ является возможность достижения надежного и быстрого функционального тестирования путем эмуляции запросов и проверки ответов непосредственно в PHP. Эту простоту тестирования часто упускают из виду, но этого не следует делать: это важная функция для создания поддерживаемых качественных приложений. Создание модульных и функциональных тестов в приложении должно быть максимально простым и быстрым, и одна из ролей фреймворка - помогать вам на этом уровне.
Чрезвычайно важно иметь надежные и быстрые автоматизированные тесты:
- Разработчики более склонны часто запускать их для проверки приложения, позволяя им обнаруживать проблемы на раннем этапе, и чем раньше они обнаружат проблему, тем меньше это будет стоить проекту.
- Это побуждает разработчиков заботиться о наборе тестов, не позволяя ему гнить и пытаясь улучшить охват каждой функции.
- Это увеличивает продуктивность, ограничивая время, затрачиваемое на проверку процессов непрерывной интеграции, обеспечивая более быстрые проверки и более быстрые слияния.
Однако даже в Symfony ваш набор тестов со временем может стать медленным или ненадежным. В этой статье я хотел бы предложить некоторые идеи и инструменты для увеличения скорости и надежности вашего набора тестов Symfony с помощью Doctrine.
Надежность
Надежность - это возможность запускать набор тестов несколько раз, при этом тест не будет успешным или неудачным только определенное время. Наличие надежного набора тестов означает, что вы уверены, что сбой в одном из ваших тестов связан с вашим кодом, а не со стабильностью самого набора тестов.
Создание надежного набора тестов состоит в основном в возможности сбрасывать состояние приложения в исходное состояние после каждого теста. Обычно состояние нашего приложения находится в двух основных местах: база данных и файловая система.
Сброс базы данных после каждого теста
Во многих своих проектах я использую два пакета Doctrine, посвященных тестам базы данных:
- DoctrineFixturesBundle - чрезвычайно полезный пакет, который помогает вам создавать и сбрасывать поддельные данные в вашей базе данных с помощью одной команды. С помощью этого пакета вы сможете подготовить набор известных сущностей в своей базе данных для использования в функциональных тестах.
- В дополнение к фикстурам я обычно использую DoctrineTestBundle, который обертывает ваше соединение с базой данных и запускает транзакцию перед каждым тестом, а затем откатывает ее после него. Этот метод - простой способ сбросить состояние вашей базы данных между тестами с помощью встроенной и чрезвычайно быстрой функции базы данных.
Использование Flysystem для абстрагирования файловой системы
Отличный способ создать надежные тесты для кода, использующего файловую систему, - это использовать абстракцию для файловой системы. Эта абстракция позволит вам настроить другое хранилище для ваших файлов в тестовой среде.
Мне лично нравится Flysystem: он очень хорошо продуман и прост в использовании. В нескольких строках конфигурации вы можете использовать хранилище памяти в своих тестах, которое будет работать намного быстрее и не будет сохранять состояние между тестами:
composer require league/flysystem composer require --dev league/flysystem-memory # config/services.yaml services: League\Flysystem\AdapterInterface: class: League\Flysystem\Adapter\Local arguments: ['%kernel.project_dir%/storage'] League\Flysystem\FilesystemInterface: class: League\Flysystem\Filesystem # config/services_test.yaml services: League\Flysystem\AdapterInterface: class: League\Flysystem\Memory\MemoryAdapter # In a controller public function index(FilesystemInterface $filesystem) { $filesystem->put('foo.txt', 'bar'); // ... }
Производительность
Работая над разными проектами, я обнаружил несколько советов по увеличению скорости моих тестовых наборов.
Максимальное использование модульных тестов
Это довольно очевидный способ повысить производительность тестов, но о нем часто забывают: использование модульных тестов намного быстрее, чем использование функциональных тестов.
Я обычно рассматриваю функциональные тесты как интеграционные тесты: тесты, обеспечивающие хорошее согласование всех компонентов вашего приложения.
Когда мы создаем тесты, мы хотим убедиться, что все функции в каждом возможном случае выполнения работают правильно. Для этого важно перечислить все возможные варианты выполнения, чтобы убедиться, что вы написали тест для каждого из них.
Когда я пишу тесты, я обычно использую один или несколько функциональных тестов для наиболее распространенных случаев выполнения и модульные тесты для остальных. Реальным примером этого является то, как я тестирую безопасность: вместо тестирования всех возможных ролей в каждом возможном случае с использованием функциональных тестов, которые были бы очень медленными, я предпочитаю проверить своего избирателя безопасности с помощью модульного теста, а затем протестировать наиболее частые случаи использования этого избирателя с помощью функционального теста. Если избиратель правильно вызывается на каждой странице (что обеспечивается функциональным тестом), и если каждый случай роли / контекста, обрабатываемый избирателем, проверяется модульным тестом, я знаю, что мои проверки безопасности безопасны, а мой набор тестов все еще быстро. Я напишу об этом статью в ближайшие недели, следите за обновлениями :)!
Использование более простого кодировщика безопасности в тестовой среде
Кодировщики безопасности описывают, как записывать и проверять пароли пользователей из базы данных. В производстве вы должны использовать bcrypt:
# config/packages/security.yaml security: encoders: App\Entity\User: bcrypt
При тестировании наличие безопасного кодировщика вовсе не обязательно, а bcrypt - довольно медленный алгоритм. Простой способ повысить скорость функциональных тестов - использовать вместо этого md5:
# config/packages/test/security.yaml security: encoders: App\Entity\User: algorithm: md5 encode_as_base64: false iterations: 0
Использование профилировщика для анализа производительности ваших тестов
Как мы видели ранее, быстрые тесты чрезвычайно важны для продуктивности разработчиков проекта: увеличение скорости вашего набора тестов - отличное вложение для качества и производительности проекта.
Я регулярно использую два основных инструмента для анализа и повышения производительности моих тестов:
- PHPUnit Pretty Printer, небольшая библиотека, предоставляющая принтер PHPUnit для отображения времени выполнения каждого теста.
- Blackfire, замечательный инструмент для профилирования, позволяющий находить точные вызовы функций и пути кода, которые отнимают больше всего времени. Я обычно использую его в сочетании с PHPUnit Pretty Printer, в конкретном тесте, который, как я обнаружил, был медленным:
blackfire run phpunit --filter MySlowTest
Есть ли у вас дополнительные идеи / советы, которые, по вашему мнению, следует добавить в статью? Не бойтесь предлагать их в комментариях!