Это примерно тот день, когда я потратил оптимизацию своих тестов вместо своего приложения. Я просто не мог больше этого терпеть. Выполнение моих тестов API займет более 10 минут. Моей первой попыткой оптимизировать этот процесс было разделение тестов на группы, но это быстро вышло из-под контроля. Итак, я наконец сел и понял, почему тесты были медленными, и многому научился в процессе!
Прочтите эту статью о моей первой попытке заставить это работать здесь.
Задний план
Небольшая предыстория этого приложения. Я использую пакет hyn \ multi-tenant для Laravel. В настоящее время клиент создается перед каждым тестом, и происходит следующее:
- Информация об арендаторах генерируется и сохраняется в базе данных системы.
- База данных создана
- Имя пользователя и пароль генерируются из ключа приложения Laravel.
- Таблицы клиентов перенесены в эту новую БД.
- Таблицы арендаторов заполнены
В моей локальной настройке разработчика этот процесс займет около 30 секунд на тест.
Я подумал, как сделать это более эффективным. Первое, о чем я подумал, - это создать и поддерживать единого клиента на протяжении всех тестов. Таким образом, я просто запускаю tenancy:migrate:fresh между тестами, чтобы получить свежие данные. Я обнаружил, что это мало помогло, потому что на эти тесты большую часть времени уходило удаление всех таблиц клиентов и их воссоздание.
Следующей моей мыслью было попробовать транзакции с базой данных. Таким образом, база данных будет откатываться между тестами. Я проверил это и БИНГО! Теперь я ожидал около 5 секунд на тест. С этим намного проще работать. Давайте перейдем к коду.
Код
Я использую черту, которая обрабатывает все операции тестирования арендатора. Первой задачей было встроить логику в черту, которая создала бы арендатора, если бы он еще не существовал. См. Следующий метод:
protected function checkTenancy()
{
if ($this->hostnames->findByhostname($this->tenantUrl)) {
$this->setUpHostnames();
$this->setUpWebsites();
} else {
$this->artisan('migrate:fresh', [
'--no-interaction' => 1,
'--force' => 1
]);
$this->setUpHostnames(true);
$this->setUpWebsites(true, true);
}
}
Таким образом, он просто проверяет, существует ли уже URL-адрес клиента, а затем загружает имя хоста и веб-сайт. Если арендатора не существует, он выполняет новые миграции в системной базе данных. Затем он создает новое имя хоста и веб-сайт и сохраняет их в системной базе данных. Это то, что делают логические флаги (true, true).
Теперь нам нужно настроить транзакцию базы данных для системной базы данных. В пакете hyn \ multi-tenant есть встроенные средства для этого, поэтому я использовал это.
protected function setUpTenancy()
{
$this->websites = app(WebsiteRepository::class);
$this->hostnames = app(HostnameRepository::class);
$this->connection = app(Connection::class);
$this->checkTenancy();
$this->connection->system()->beginTransaction();
}
Здесь мы берем экземпляры репозиториев Website и Hostname и класса Connection. Мы запускаем checkTenancy() метод, который мы создали ранее. Затем мы начинаем транзакцию с системной базой данных. Легкий! Мы будем использовать этот метод для инициализации системы аренды на основе наших тестов.
Еще нужно знать, что мы можем использовать транзакции с базой данных Tenant. Используя такие методы:
protected function activateTenant()
{
app(Environment::class)->tenant($this->website);
$this->connection->get()->beginTransaction();
}
protected function rollbackTenant()
{
if ($this->connection->exists() && $this->connection->get()->transactionLevel() > 0) {
$this->connection->get()->rollBack();
}
}
Мы начнем транзакцию после того, как активируем арендатора. Затем мы можем откатить соединение, вызвав rollbackTenant(). Я вызываю это в методе cleanupTenancy().
Вот и вся черта.
Еще один метод, на который следует обратить внимание, - это cleanUpTenancy(). Здесь мы откатываем транзакцию.
Пример использования
Вот пример того, как я использовал его в своих тестах. Я использовал средства тестирования SetUp() и tearDown() для инициализации и очистки аренды. Вот TenantTestCase, от которого ваши тесты клиента могут наследовать.
Мы просто вызываем метод setUpTenancy() в ловушке setUp и активируем клиента, чтобы все запросы использовали подключение клиента. У меня также есть duringSetUp() метод, который можно использовать в тестах.
Вот настоящий тест.
Как видите, это похоже на обычный тест, но за кулисами используется мультитенантный пакет.
Теперь мы можем запустить./vendor/bin/phpunit — testdox и увидеть зеленые галочки. Гораздо лучше вовремя!

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