在PHPUnit中模拟紧密耦合的对象

时间:2017-06-17 13:15:02

标签: php unit-testing pdo phpunit

对于练习,我正在创建自己的数据库包装器和查询构建器。创建DatabaseConnection对象时,会在此对象中创建一个新的PDO对象。

我有一个Database类,它依赖于DatabaseConnection对象。该类构建查询并执行它们。

我的DatabaseConnection课程如下:

use PDO;

class DatabaseConnection
{
    /**
     * @var PDO $pdo The PDO connection object
     */
    private $pdo;

    public function __construct(array $credentials)
    {
        # Logic with $credentials to check on data
        $dsn = ...;
        # Open the connection
        $this->pdo = new \PDO($dsn, $credentials['username'], $credentials['password'], $options);
    }

    /**
     * Checks whether the connection is open or closed
     *
     * @return bool Whether the connection is open or closed
     */
    public function isConnected() : bool
    {
        return $this->pdo !== null;
    }

    /**
     * Retrieves the PDO connection object
     *
     * @return PDO The PDO connection object
     */
    public function getPDO() : PDO
    {
        return $this->pdo;
    }

    /**
     * Closes the connection
     */
    public function __destruct()
    {
        $this->pdo = null;
    }
}

在对此进行测试时,我遇到了一个问题,即我总是需要在每台计算机上使用相同的登录凭据创建一个具有相同登录凭据的数据库。

对于我的研究,我发现我可以通过

创建一个模拟对象
$pdo = $this->getMockBuilder('PDO')
    ->disableOriginalConstructor()
    ->getMock();

这里的问题是我无法将$pdo对象注入我的DatabaseConnection类。

我做错了什么?如何在不使用实际数据库的情况下通过模拟数据来测试此类?

1 个答案:

答案 0 :(得分:1)

对于mock,创建 DatabaseConnection 的子类型,覆盖构造函数并使PDO对象可注入。然后,您可以模拟PDO对象并为模拟操作创建完整的模拟。

在构造函数中使用"news"是一种气味btw。,你的构造函数不应该做真正的工作。

正如您在测试时发现的那样,这会使您的类型难以测试。自己创建一个模拟(通过写出来)可以减轻黑暗的天空。

您可能也不希望尽早初始化资源,但要尽可能晚地连接到数据库。这也会使新的构造函数移出。