Тесты PHPUnit и Doctrine, слишком много соединений

У меня проблемы с "слишком большим количеством соединений" для тестов PHPUnit для ZF3 и Doctrine, потому что я выполняю ~200 тестов за выполнение PHPUnit. Я уже нашел некоторые вопросы и ответы по переполнению стека, но не из этих работ.

Мои настройки: ZF2/ZF3, Doctrine 2 и PHPUnit.

У меня есть базовый тестовый класс для всех тестов, и функции setUp и tearDown выглядят так:

public function setUp()
{
    $this->setApplicationConfig(Bootstrap::getConfig());
    Bootstrap::loadAllFixtures();
    if (!static::$em) {
        echo "init em";
        static::$em = Bootstrap::getEntityManager();
    }
    parent::setUp();
    ....
}

public function tearDown()
{
    parent::tearDown();
    static::$em->flush();
    static::$em->clear();
    static::$em->getConnection()->close();
    $refl = new \ReflectionObject($this);
    foreach ($refl->getProperties() as $prop) {
        if (!$prop->isStatic() && 0 !== strpos($prop->getDeclaringClass()->getName(), 'PHPUnit_')) {
            $prop->setAccessible(true);
            $prop->setValue($this, null);
        }
    }
    gc_collect_cycles();
}

public static function (Bootstrap::)loadAllFixtures()
{
    static::$em->getConnection()->executeUpdate("SET foreign_key_checks = 0;");
    $loader = new Loader();
    foreach (self::$config['data-fixture'] as $fixtureDir) {
        $loader->loadFromDirectory($fixtureDir);
    }
    $purger = new ORMPurger(static::$em);
    $executor = new ORMExecutor(static::$em, $purger);
    $executor->execute($loader->getFixtures());
    $executor = null;
    $purger = null;
    static::$em->getConnection()->executeUpdate("SET foreign_key_checks = 1;");
    static::$em->flush();
    static::$em->clear();
}

Я отслеживаю свой локальный сервер MySQL с помощью innotop, и количество подключений увеличивается.

У тебя есть идеи, что мне не хватает?

Спасибо Александр

Обновление 14.02.2017: я изменил функции для использования static::$em и добавил Bootstrap::loadAllFixtures метод.

Если я добавлю static::$em->close() в tearDown метод, все последующие проверки завершаются неудачно с сообщением типа "EntityManager уже закрыт". echo "init em"; только один раз и отображается для первого теста. Есть ли возможность проверить, открывает ли мое приложение соединения, не закрывая их? Мои тестовые случаи основаны на AbstractHttpControllerTestCase

1 ответ

Я тоже сталкивался с этой проблемой. Следуя советам в документации PHPUnit, я сделал следующее:

final public function getConnection()
{
    if ($this->conn === null) {
        if (self::$pdo == null) {

            //We get the EM from dependency injection container
            $container = $this->getContainer();
            self::$pdo = $container->get('Doctrine.EntityManager')->getConnection()->getWrappedConnection();
        }
        $this->conn = $this->createDefaultDBConnection(self::$pdo, 'spark_api_docker');
    }

    return $this->conn;
}

В то время как self:$pdo был разделен, число "threads_connected", когда я заметил show status like '%onn%'; в моей базе данных подкрались, пока не достигли предела.

Я нашел два решения этого:


1) Закройте соединение после каждого теста

public function tearDown()
{
    parent::tearDown();

    //You'll probably need to get hold of our entity manager another way
    $this->getContainer()->get('Doctrine.EntityManager')->getConnection()->close();
}

главное не ставить self::$pdo в null, Я видел это как рекомендацию в другом месте, но нет смысла устанавливать его как статическое свойство, а затем сбрасывать его после каждого теста.

Это работает мои закрывающие соединения, которые больше не нужны. Когда тестовый сценарий завершен, если вы не закрыли соединение, оно останется открытым до тех пор, пока сценарий не завершится (т.е. PHPUnit завершит выполнение теста). Поскольку вы создаете новое соединение для каждого тестового случая, количество соединений увеличивается.


2) Запустите каждый тест в отдельном потоке PHP

Это подход кувалдой. Скорее всего, это повлияет на скорость ваших тестов в той или иной степени. В вашем phpunit.xml`:

<?xml version="1.0" encoding="UTF-8"?>

<phpunit
    ...
    processIsolation = "true"
    >
    ...
</phpunit>

Возвращаясь к совету PHPUnit, хранение соединения и PDO помогает не создавать новые соединения для каждого теста, но не помогает, когда у вас много тестовых случаев. Каждый тестовый пример создается в одном и том же потоке, и каждый создает новое соединение.

Ваш метод tearDown выглядит так, как будто должен сработать. Я просто делаю это и никогда не сталкивался с этой проблемой

protected function tearDown()
{
    parent::tearDown();

    $this->em->close();
    $this->em = null; 
}

Что делает метод Bootstrap::loadAllFixtures? Есть ли там какое-либо соединение с БД, которое может быть упущено?

Другие вопросы по тегам