【问题标题】:PHPUnit testing a protected static method that uses pdoPHPUnit 测试使用 pdo 的受保护静态方法
【发布时间】:2018-09-13 20:19:00
【问题描述】:

我对 TDD 很陌生。我正在使用 phpunit 7.4x-dev。我有以下抽象类,我正在尝试为其开发单元测试。

use PDO;

abstract class Model {

    protected static function getDB() {
        static $db = null;
        if ($db === null) {

                $db = new PDO(ConfigDatabase::DSN, ConfigDatabase::USER, ConfigDatabase::PASSWORD);
                $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

        }
        return $db;
    }


}

我创建了以下测试来解决处理静态受保护方法的需要。如果我提供“ConfigureDatabase”类,它就可以工作。

use PHPUnit\Framework\TestCase;

class ModelTest extends TestCase {

    function newMockClass(){
        $stub = new class() extends Model{
            function getStaticMethod($methodName){
                return self::$methodName();
            }
        };        
        return $stub;

    }

    public function testDatabaseExists() {
         $stub = $this->newMockClass();
         $db = $stub->getStaticMethod('getDB');
         $this->assertInstanceOf(PDO::class,$db);         
    }

}

由于我不希望我的测试依赖于任何实际的数据库,我将如何伪造对 PDO 的调用。

【问题讨论】:

  • 最简单的方法是不使用静态方法。更好地使用依赖注入。
  • 你能举个例子吗?
  • 将 PDO 对象作为类的构造函数的参数传递。

标签: pdo phpunit


【解决方案1】:

根据 Dormilich 的建议,我开发了一个数据库接口,以防我以后决定不想使用 PDO。

interface CRUDImp {
    function __construct($datbaseBridgeLikePDO);
    ...
}

接下来我为构造函数编写了测试。我使用 setup 来确保我从一个新的 \PDO 模拟开始。

    class PDOWrapperTest extends TestCase {
    private $pdoMock;
    private $db;

    function setup() {
        $this->pdoMock = $this->createMock('\PDO');
        $this->db = new PDOWrapper($this->pdoMock);
    }

        public function testWrapperExists() {
            $this->pdoMock->method('getAttribute')->willReturn(\PDO::ERRMODE_EXCEPTION);

            $db = new PDOWrapper($this->pdoMock);
            $x = $db instanceof CRUDImp;

            $this->assertTrue($x);
        }

        /**
         * @expectedException \Exception
         */
        public function testNonPDOPassedToConstructor() {
            $mock = $this->createMock('\Exception');
            $x = new PDOWrapper($mock);
        }
    ...
    }

由于 PHP 是松散类型的,我检查以确保传递给构造函数的类是 \PDO 的实例。我实现的具体类如下

class PDOWrapper implements CRUDImp {

    private $pdo;
    private $dataOutputType = \PDO::FETCH_ASSOC;

    public function __construct($pdo) {

        if (!($pdo instanceof \PDO)) {
            throw new \Exception("PDOWrapper must be passed instance of \PDO");
        }

        $attr_Errmode = $pdo->getAttribute(\PDO::ATTR_ERRMODE);
        if ($attr_Errmode !== \PDO::ERRMODE_EXCEPTION) {
            $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
        }
        $this->pdo = $pdo;
    }
...
}

现在我有一个独立的数据库包装器,原来的模型测试现在很简单,不再需要。抽象类Model修改如下:

abstract class Model {
    protected $database=null;

    function __construct(CRUDWrapper $database) {
        $this->database = $database;
    }
...
}

所以对于那些不熟悉依赖注入的人,我发现以下链接很有帮助:

http://php-di.org/doc/understanding-di.html https://codeinphp.github.io/post/dependency-injection-in-php/ https://designpatternsphp.readthedocs.io/en/latest/Structural/DependencyInjection/README.html

希望这可以缩短某人的工作。

【讨论】:

    猜你喜欢
    • 2021-12-24
    • 2011-09-09
    • 2022-08-05
    • 2014-12-19
    • 1970-01-01
    • 2015-10-13
    • 2011-06-27
    • 2011-06-23
    相关资源
    最近更新 更多