如何在PHPUnit和ZF3中减少测试中的数据库连接数?

aut*_*tix 16 php phpunit pdo unit-testing zend-framework3

我正在使用.编写Zend Framework 3应用程序的集成/数据库测试

  • zendframework/zend-test 3.1.0,
  • phpunit/phpunit 6.2.2,和
  • PHPUnit的/ DbUnit的 3.0.0

我的测试由于失败而失败

Connect Error: SQLSTATE[HY000] [1040] Too many connections
Run Code Online (Sandbox Code Playgroud)

我设置了一些断点并查看了数据库:

SHOW STATUS WHERE `variable_name` = 'Threads_connected';
Run Code Online (Sandbox Code Playgroud)

我实际上已经看到了100打开的连接.

我通过断开连接来减少它们tearDown():

protected function tearDown()
{
    parent::tearDown();
    if ($this->dbAdapter && $this->dbAdapter instanceof Adapter) {
        $this->dbAdapter->getDriver()->getConnection()->disconnect();
    }
}
Run Code Online (Sandbox Code Playgroud)

但我仍然有过度80开放的联系.

如何将测试中的数据库连接数减少到可能的最小值?


更多信息

(1)我有很多测试,其中我dispatch是一个URI.每个此类请求都会导致至少一个数据库请求,从而导致新的数据库连接.这些连接似乎没有关闭.这可能会导致最多的连接.(但是我还没有找到一种方法让应用程序在处理请求后关闭连接.)

(2)其中一个问题可能是我对数据库的测试:

protected function retrieveActualData($table, $idColumn, $idValue)
{
    $sql = new Sql($this->dbAdapter);
    $select = $sql->select($table);
    $select->where([$table . '.' . $idColumn . ' = ?' => $idValue]);
    $statement = $sql->prepareStatementForSqlObject($select);
    $result = $statement->execute();
    $data = $result->current();
    return $data;
}
Run Code Online (Sandbox Code Playgroud)

$this->dbAdapter->getDriver()->getConnection()->disconnect()之前的呼唤return没有给予任何东西.

测试方法中的用法示例:

public function testInputDataActionSaving()
{
    // The getFormParams(...) returns an array with the needed input.
    $formParams = $this->getFormParams(self::FORM_CREATE_CLUSTER);

    $createWhateverUrl = '/whatever/create';
    $this->dispatch($createWhateverUrl, Request::METHOD_POST, $formParams);

    $this->assertEquals(
        $formParams['whatever']['some_param'],
        $this->retrieveActualData('whatever', 'id', 2)['some_param']
    );
}
Run Code Online (Sandbox Code Playgroud)

(3)另一个问题可能出在PHPUnit(或我的配置?)中.(Striken,因为"PHPUnit没有做任何与数据库连接相关的事情.",请参阅评论.)无论如何,即使它不是PHPUnit问题,事实是,在行之后

$testSuite = $configuration->getTestSuiteConfiguration($this->arguments['testsuite'] ?? null);
Run Code Online (Sandbox Code Playgroud)

PHPUnit\TextUI\Command我得到31新的联系.

ter*_*ško 8

干净和正确的方法

如果"您的代码以难以测试的方式编写",这似乎是一个问题.数据库连接应该由DIC处理或(在某些连接池的情况下)一些specialize类.基本上,包含的类retrieveActualData()应该将Sql实例作为构造函数中的依赖项传递.

相反,看起来您的Sql类是一个有害的PDO包装器,它(很可能)在您创建实例时建立了数据库连接.相反,您应该在多个类之间共享相同的PDO实例.这样,您既可以控制已建立的连接数量,又可以在(某些)隔离中测试代码.

因此,主要的解决方案是 - 您的代码很糟糕,但您可以清理它.

不要将new片段放在执行树的深处,而是将连接作为依赖项传递并共享.

通过这种方式,您可以使用各种模拟和存根来帮助您隔离测试结构.

在DB绑定逻辑和gremlins的情况下

但是你应该考虑一个更实际的方面.在集成测试中使用SQLite而不是真实数据库.PDO支持该选项(您只需为测试代码提供不同的DSN).

如果您切换到使用SQLite作为"测试数据库",您将能够拥有一个定义良好的数据库状态(多个),您可以使用它来测试代码.

你有类似文件的东西integration-002.db,它包含准备好的数据库状态.在集成测试的引导程序中,您只需将准备好的sqlite数据库文件复制integration-0902.dblive-002.db并运行所有测试.

use PHPUnit\Framework\TestCase;

final class CombinedTest extends TestCase
{
    public static function setUpBeforeClass()
    {
        copy(FIXTURE_PATH . '/integration-02.db', FIXTURE_PATH . '/live-02.db');
    }


    // your test go here

}
Run Code Online (Sandbox Code Playgroud)

这样您就可以更好地控制持久性状态,并且测试运行速度会快得多,因为不涉及网络堆栈.

当发现新错误时,您还可以准备任意数量的测试数据库并添加新数据库.此方法将允许您在数据库中重新创建更复杂的方案,甚至模拟数据损坏.

您可以在项目中实际看到此方法.


PS来自个人经验 - 在集成测试中使用SQLite还可以提高SQL代码的一般质量(如果您不使用查询构建器,而是编写自定义数据映射器).因为它迫使您考虑SQLite中针对MariaDB或PostgreSQL的可用功能之间的差异.但这是"你的里程可能会有所不同"之一.

PPS可以同时使用两种建议的方法,因为它们只会相互增强.