如何减少PHPUnit和ZF3中的测试中的数据库连接数? [英] How to reduce the number of database connections in tests in PHPUnit and ZF3?

查看:47
本文介绍了如何减少PHPUnit和ZF3中的测试中的数据库连接数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm writing integration/database tests for a Zend Framework 3 application by using

  • zendframework/zend-test 3.1.0
  • phpunit/phpunit 6.2.2
  • phpunit/dbunit 3.0.0
  • zendframework/zend-test 3.1.0,
  • phpunit/phpunit 6.2.2, and
  • phpunit/dbunit 3.0.0

由于

Connect Error: SQLSTATE[HY000] [1040] Too many connections

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

I set some breakpoints and took a look into the database:

SHOW STATUS WHERE `variable_name` = 'Threads_connected';

我实际上已经查看过100个打开的连接.

And I've actually seen over 100 opened connections.

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

I've reduced them by disconnecting in the tearDown():

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

但是我仍然有超过80个打开的连接.

But I still have over 80 opened connections.

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

更多信息

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

(1) I have a lot of tests, where I dispatch a URI. Every such request causes at least one database request, that cause a new database connection. These connections seem not to be closed. This might cause the most connections. (But I haven't yet found a way to make the application close the connections after the request is processed.)

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

(2) One of the issues might be my testing against the database:

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()之前的$this->dbAdapter->getDriver()->getConnection()->disconnect()调用没有任何作用.

But the call of the $this->dbAdapter->getDriver()->getConnection()->disconnect() before the return gave nothing.

测试方法中的用法示例:

Example of usage in a test method:

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(或它的配置?)中.(删除该字是因为"PHPUnit不执行与数据库连接有关的任何操作.",请参见评论.)无论如何,即使这不是PHPUnit问题,事实是,在该行之后

(3) Another issue might be in the PHPUnit (or my configuration of it?). (Striken out, because "PHPUnit does not do anything related to database connections.", see this comment.) Anyway, even if it's not a PHPUnit issue, the fact is, that after the line

$testSuite = $configuration->getTestSuiteConfiguration($this->arguments['testsuite'] ?? null);

PHPUnit\TextUI\Command 中获得31新连接.

推荐答案

干净的&正确的方法

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

The clean & proper approach

This seems to be an issue if "your code is written in a way that is hard to test". The DB connection should be either handled by DIC or (in case of some connection pool) some specialize class. Basically, the class, that contains retrieveActualData() should have the Sql instance being passed as a dependency in a constructor.

相反,您的Sql类似乎是有害的PDO包装器,(很可能)在您创建实例时就建立了数据库连接.相反,您应该在多个类之间共享同一PDO实例.这样,您既可以控制建立的连接数量,又可以通过某种方式测试您的代码.

Instead, it looks like your Sql class is a harmful PDO wrapper, that (most likely) established a DB connection whenever you create an instance. Instead you should be sharing same PDO instance among multiple classes. That way you can both control the amount of the connections established and have a way to test you code in (some) isolation.

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

So, the primary solution is - your code is bad, but you can clean it up.

与其将new片段分散在执行树的深处,不如将连接作为依赖项进行传递并共享它.

Instead of having new snippets sprinkled deep in your execution tree, pass the connection as a dependency and share it.

通过这种方式,您的测试可以逐步使用各种模拟和存根,从而帮助您隔离经过测试的结构.

This way you tests can move towards use of various mocks and stubs, that help you isolate the tested structures.

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

But there is also a more practical aspect, that you should consider. Use SQLite instead of real database in your integration tests. PDO support that option (you just have to provide a different DSN for your test code).

如果您改用SQLite作为测试数据库",则将能够有一个定义良好的数据库状态(多个),可以根据它们对代码进行测试.

If you switch to using SQLite as your "testing DB", you will be able to have a well defined DB states (multiple) against which you can test your code.

您有类似文件integration-002.db的内容,其中包含准备好的数据库状态.在集成测试的引导程序中,您只需将准备好的sqlite数据库文件从integration-0902.db复制到live-002.db,然后运行所有测试.

You have something like file integration-002.db, which contains the prepared database state. In the bootstrap of your integration tests, you just copy over that prepared sqlite database-files from integration-0902.db to live-002.db and run all the tests.

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

}

这样,您将更好地控制持久性状态 ,并且由于不涉及网络堆栈,因此测试的运行速度将大大提高.

That way you will gain both better control over your persistence state and your tests will run a lot faster, since there is no network stack involved.

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

You can also prepare any number of test-databases and add new ones, when a new bug is discovered. This approach will let you recreate more complex scenarios in your DB and even simulate data corruption.

您可以在项目中看到这种方法的实践.

You can see this approach in practice in this project.

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

P.S. from personal experience - using SQLite in the integration tests also improves the general quality of ones SQL code (if you are not using query builders, but instead are writing custom data-mappers). Because it forces you to consider the differences between available functionality in SQLite against MariaDB or PostgreSQL. But it's one of those "your mileage may vary" things.

P.P.S..您可以同时使用两种建议的方法,因为它们只会彼此增强.

P.P.S. you can utilize both of the suggested approaches as the same time, since they will only enhance each-other.

这篇关于如何减少PHPUnit和ZF3中的测试中的数据库连接数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆