dongtiao0279
dongtiao0279
2018-07-06 09:43
浏览 40
已采纳

单元测试接受字符串参数以创建PDO对象的PHP类

I have a class (PDOMySQLForeignKeysToURLsService) that does exactly what it says on the tin, it takes a JSON encoded database entity, compares it to a configuration file and transforms any foreign key fields into URLs linking to other RESTful resources. i.e.

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

assuming person_id is a foreign key in the database, will become something like

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

because this service itself is initalized from a configuration file it can only accept text in it's constructor,

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

everything seems to running fine and dandy, but now I want to write some unit tests for this class, and part of it requires that a PDO object is instantiated and the INFORMATION_SCHEMA table queried like below

  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

The method in which this occurs is in an implementation of an interface Resolvable, defining function resolveRequest(ServiceRequest $request, ServiceResponse $response)

I basically have no idea how to go around testing all of this besides setting up a schema/some tables in the tests set up? I have heard from my peers that this is bad practice and does not constitute unit testing but rather integration testing - so my question is how could I go about unit testing this method, is unit testing even appropriate here? The class is by definition very dependant on MySQL (and PDO). Should I write some kind of adapter around PDO and use a DI framework to inject it, and interface with that rather than PDO directly? I'm pretty lost at what to do, tbh, and don't understand the idea/how to mock dependencies that are only used within the scope of a method

图片转代码服务由CSDN问答提供 功能建议

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

  [
 {
“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  “
} 
] 
   
 
 

因为此服务本身是从配置文件初始化的,它只能接受其构造函数中的文本, \ n

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

一切似乎都在运行 很好,但现在我想为这个类编写一些单元测试,其中一部分要求实例化 PDO 对象,并在下面查询 INFORMATION_SCHEMA 表 / p>

  SELECT 
 * 
 FROM 
 INFORMATION_SCHEMA.KEY_COLUMN_USAGE 
 WHERE 
 TABLE_SCHEMA =?  AND 
 CONSTRAINT_NAME!=“PRIMARY”AND 
 REFERENCED_TABLE_NAME不为空且
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
引用_COLUMN_NAME不是空
   
 
 

执行此操作的方法是在实现中 接口 Resolvable ,定义 function resolveRequest(ServiceRequest $ request,ServiceResponse $ response)

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

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

1条回答 默认 最新

  • dongxili9934
    dongxili9934 2018-07-11 03:14
    已采纳

    Even if you can't inject the PDO instance, there are still ways to unit test this - it just requires a little creativity. Probably the easiest way to unit test what you're describing would be to create a testable subclass that allows for injection. The exact details would depend on how your class is implemented, but a simple example might look something like this:

    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;
        }
    }
    

    The TestablePDOMySQLForeignKeysToURLsService class would live next to your tests and you would use it in your test cases instead of the production class. The idea is that the "testable" class is a minimal extension of the production class - just enough to let you inject test doubles. This lets you test the logic from the production class and mock the database calls in your test without having to change the interface to the production class. It's kind of cheating, but it gets the job done.

    点赞 评论

相关推荐