【问题标题】:Unit testing a PHP class that accepts string arguments to create a PDO object对接受字符串参数以创建 PDO 对象的 PHP 类进行单元测试
【发布时间】:2018-12-14 21:24:13
【问题描述】:

我有一个类 (PDOMySQLForeignKeysToURLsService),它完全按照它在 tin 上说的做,它采用 JSON 编码的数据库实体,将其与配置文件进行比较,并将任何外键字段转换为链接到其他 RESTful 资源的 URL。即

[
  {
    "ID": "1",
    "person_id": "1",
    "pc_name": "my_computer"
  }
]

假设person_id是数据库中的外键,会变成类似

[
  {
    "ID": "1",
    "person_id": "http://localhost/api/db/person/1",
    "pc_name": "my_computer"
  }
]

因为这个服务本身是从一个配置文件初始化的,所以它只能在它的构造函数中接受文本,

public function __construct(string $pdoDSN, string $username, string $password, string $tablename, string $schemaname)

一切似乎都运行良好且花花公子,但现在我想为这个类编写一些单元测试,其中一部分需要实例化 PDO 对象并像下面那样查询 INFORMATION_SCHEMA

  SELECT 
    *
  FROM
    INFORMATION_SCHEMA.KEY_COLUMN_USAGE
  WHERE
    TABLE_SCHEMA = ? AND
    CONSTRAINT_NAME != "PRIMARY" AND
    REFERENCED_TABLE_NAME IS NOT NULL AND
    REFERENCED_COLUMN_NAME IS NOT NULL

发生这种情况的方法是在接口Resolvable的实现中,定义function resolveRequest(ServiceRequest $request, ServiceResponse $response)

除了在测试设置中设置架构/一些表之外,我基本上不知道如何测试所有这些?我从同行那里听说这是不好的做法,不构成单元测试,而是集成测试——所以我的问题是我该如何对这种方法进行单元测试,单元测试在这里是否合适?根据定义,该类非常依赖于 MySQL(和 PDO)。我是否应该围绕 PDO 编写某种适配器并使用 DI 框架来注入它,并与它而不是直接与 PDO 交互?我很迷茫该怎么做,tbh,并且不理解这个想法/如何模拟仅在方法范围内使用的依赖项

【问题讨论】:

    标签: php mysql unit-testing pdo phpunit


    【解决方案1】:

    即使您不能注入 PDO 实例,仍然有办法对此进行单元测试 - 它只需要一点创造力。对您所描述的内容进行单元测试的最简单方法可能是创建一个允许注入的可测试子类。确切的细节取决于你的类是如何实现的,但一个简单的例子可能看起来像这样:

    class PDOMySQLForeignKeysToURLsService {
        protected $pdo;
    
        public function __construct(string $pdoDSN, string $username, string $password, string $tablename, string $schemaname) {
            $this->pdo = $this->createPdo(...);
        }
    
        // Encapsulate the PDO creation so that the subclass can override it.
        protected function createPdo(...) {
            return new PDO(...);
        }
    
        // Other methods omitted...
    }
    
    class TestablePDOMySQLForeignKeysToURLsService extends PDOMySQLForeignKeysToURLsService {
        // Override the constructor so you can pass in a mock PDO as the last parameter
        public function __construct(string $pdoDSN, string $username, string $password, string $tablename, string $schemaname, PDO $pdo) {
            // Save the injected mock PDO instance.
            $this->pdo = $pdo;
            parent::__construct(...);
        }
    
        // Override the creation so that you can just use the injected mock.
        protected function createPdo(...) {
            return $this->pdo;
        }
    }
    

    TestablePDOMySQLForeignKeysToURLsService 类将存在于您的测试旁边,您将在测试用例而不是生产类中使用它。这个想法是“可测试”类是生产类的最小扩展 - 刚好 足以让您注入测试替身。这使您可以测试生产类的逻辑并在测试中模拟数据库调用,而无需更改生产类的接口。这有点作弊,但它可以完成工作。

    【讨论】:

    • 我喜欢它 - 最后我屈服并实现了一个适配器并使用反射加载它,但是花了很长时间,我觉得我没有从中得到太多好处.这样做肯定会更快,更适合我的情况,无论是否作弊!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-10
    • 2017-12-11
    • 1970-01-01
    • 2013-05-22
    • 1970-01-01
    相关资源
    最近更新 更多