作者:AD社团 | 来源:互联网 | 2023-01-23 14:03
我正在使用.编写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
我设置了一些断点并查看了数据库:
SHOW STATUS WHERE `variable_name` = 'Threads_connected';
我实际上已经看到了100
打开的连接.
我通过断开连接来减少它们tearDown()
:
protected function tearDown()
{
parent::tearDown();
if ($this->dbAdapter && $this->dbAdapter instanceof Adapter) {
$this->dbAdapter->getDriver()->getConnection()->disconnect();
}
}
但我仍然有过度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;
}
但$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']
);
}
(3)另一个问题可能出在PHPUnit(或我的配置?)中.(Striken,因为"PHPUnit没有做任何与数据库连接相关的事情.",请参阅此评论.)无论如何,即使它不是PHPUnit问题,事实是,在行之后
$testSuite = $configuration->getTestSuiteConfiguration($this->arguments['testsuite'] ?? null);
在PHPUnit\TextUI\Command
我得到31
新的联系.
1> tereško..:
干净和正确的方法
如果"您的代码以难以测试的方式编写",这似乎是一个问题.数据库连接应该由DIC处理或(在某些连接池的情况下)一些specialize类.基本上,包含的类retrieveActualData()
应该将Sql
实例作为构造函数中的依赖项传递.
相反,看起来您的Sql
类是一个有害的PDO
包装器,它(很可能)在您创建实例时建立了数据库连接.相反,您应该在多个类之间共享相同的PDO实例.这样,您既可以控制已建立的连接数量,又可以在(某些)隔离中测试代码.
因此,主要的解决方案是 - 您的代码很糟糕,但您可以清理它.
不要将new
片段放在执行树的深处,而是将连接作为依赖项传递并共享.
通过这种方式,您可以使用各种模拟和存根来帮助您隔离测试结构.
在DB绑定逻辑和gremlins的情况下
但是你应该考虑一个更实际的方面.在集成测试中使用SQLite而不是真实数据库.PDO支持该选项(您只需为测试代码提供不同的DSN).
如果您切换到使用SQLite作为"测试数据库",您将能够拥有一个定义良好的数据库状态(多个),您可以使用它来测试代码.
你有类似文件的东西integration-002.db
,它包含准备好的数据库状态.在集成测试的引导程序中,您只需将准备好的sqlite数据库文件复制integration-0902.db
到live-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
}
这样您就可以更好地控制持久性状态,并且测试运行速度会快得多,因为不涉及网络堆栈.
当发现新错误时,您还可以准备任意数量的测试数据库并添加新数据库.此方法将允许您在数据库中重新创建更复杂的方案,甚至模拟数据损坏.
您可以在此项目中实际看到此方法.
PS来自个人经验 - 在集成测试中使用SQLite还可以提高SQL代码的一般质量(如果您不使用查询构建器,而是编写自定义数据映射器).因为它迫使您考虑SQLite中针对MariaDB或PostgreSQL的可用功能之间的差异.但这是"你的里程可能会有所不同"之一.
PPS可以同时使用两种建议的方法,因为它们只会相互增强.