Это примерно тот день, когда я потратил оптимизацию своих тестов вместо своего приложения. Я просто не мог больше этого терпеть. Выполнение моих тестов 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 и увидеть зеленые галочки. Гораздо лучше вовремя!

Вывод

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



Надеюсь, вы нашли эту демонстрацию полезной! Если вам понравилась эта статья, ознакомьтесь с моей серией статей о создании мультитенантного приложения.



Спасибо!