PHPUnit:如何在远程 Postgres 服务器上测试数据库交互?

2022-01-25 00:00:00 postgresql unit-testing php phpunit amazon

我正在尝试对我创建的类进行单元测试,但大多数类都处理数据库.我已经让非数据库相关的类在本地测试得很好,但是在使用数据库时我很难过,尤其是远程.该指南显示使用 PDO 访问似乎已转储到 XML 文件的本地数据库,因此它对我来说没什么用,因为我的数据库位于 Amazon 云中并使用 pg_* 函数连接到 Postgres 数据库.

I'm trying to unit test the classes I've created but the majority of the classes deal with the database. I've gotten non-database related classes to be test just fine locally, but I'm stumped when it comes to work with a database, especially remotely. The guide shows using PDO to access a local database that seems to be dumped to an XML file, so it's of little use to me since my database is in the Amazon cloud and using pg_* functions to connect to a Postgres database.

有没有类似情况的好例子,或者有人可以提供任何帮助吗?我不知道我是否应该在文件中包含我的数据库的本地版本或连接到远程服务器.如果我必须连接,我该怎么做才能让它工作?

Are there any good examples of a similar situation or can anyone offer any help? I don't know if I should have a local version of my DB in a file or connect to the remote server. If I have to connect, what do I have to do to make it work?

结论
项目架构师和我进行了调查,我们确定最好实现 ORM,因为数据库没有抽象.在此之前,数据库测试将暂停.一旦到位,我相信 PHPUnit 手册会更有意义.

Conclusion
The project architect and I did an investigation and we determined it would be best to implement an ORM, since there was no abstraction to the database. Until then, database testing will be on hold. Once in place, I'm sure the PHPUnit manual will make much more sense.

推荐答案

简短的回答是 阅读 PHPUnit 手册中关于数据库测试的 Fine Manual 条目.

关于单元测试,首先要记住的是,它需要在与所有其他组件隔离的情况下执行.通常,使用控制反转 (IoC) 技术(如 依赖注入wikipedia.当您的类在构造函数方法中明确要求它们的依赖关系时,这是一个简单的操作 模拟phpunit 那些依赖项,以便您可以单独测试剩余的代码.

The first thing to remember about unit testing is that it needs to be performed in isolation from all other components. Often, this goal is simplified using inversion of control (IoC) techniques like dependency injectionwikipedia. When your classes explicitly ask for their dependencies in the constructor methods it's a simple operation to mockphpunit those dependencies so that you can test the remaining code in isolation.

不过,与模型交互的测试代码有点不同.通常将您的模型注入到您需要访问它们的类中是不切实际或不可取的.您的模型通常是愚蠢"的数据结构,它们具有有限的功能或没有功能.因此,在其他注入的类中动态实例化模型通常是可以接受的(就可测试性而言).不幸的是,这使得测试数据库代码变得困难,因为正如 PHPUnit 文档所述:

Testing code that interacts with models is a bit different, though. Usually it's not practical or advisable to inject your models into the class in which you need to access them. Your models are generally "dumb" data structures that expose limited or no capabilities. As a result, it's generally acceptable (in terms of testability) to instantiate your models on the fly inside your otherwise-injected classes. Unfortunately, this makes testing database code difficult because, as the PHPUnit documentation notes:

[T]数据库本质上是代码的全局输入变量

[T]he database is essentially a global input variable to your code

那么,如果模型不是直接注入的,那么如何隔离和测试与数据库交互的代码呢?最简单的方法是利用 测试夹具phpunit.

So how do you isolate and test code that interacts with the database if the models aren't directly injected? The simplest way to do this is to utilize test fixturesphpunit.

既然您肯定已经在使用 PDO 或基于 PDO 构建的 ORM 库(对吗?),设置fixture 就像在基本的SQLite 数据库或XML 文件中植入数据以适应您的测试用例并在测试与数据库交互的代码时使用该特殊的数据库连接一样简单.您可以在 PHPUnit 引导文件中指定此连接,但设置 PHPUnit 数据库测试用例phpunit.

Since you're definitely already using PDO or an ORM library that builds on PDO (right?), setting up the fixtures is as simple as seeding a basic SQLite database or XML file with data to accommodate your test cases and using that special database connection when you test the code that interacts with the database. You could specify this connection in your PHPUnit bootstrap file, but it's probably more semantically appropriate to setup a PHPUnit Database TestCasephpunit.

测试数据库代码的公认最佳实践步骤(这些也反映在有关数据库测试的 PHPUnit 文档中):

The generally accepted best practice steps for testing DB code (these are also echoed in the PHPUnit documentation on DB testing):

  1. 设置夹具
  2. 正在测试的运动系统
  3. 验证结果
  4. 拆解

因此,总而言之,您需要做的就是创建一个虚拟"数据库装置,并让您的代码与已知数据进行交互,而不是与您将在生产中使用的实际数据库进行交互.此方法允许您成功隔离被测代码,因为它处理已知数据,这意味着您可以对数据库操作的结果做出特定/可测试的断言.

So, to summarize, all you need to do is create a "dummy" database fixture and have your code interact with that known data instead of an actual database you would use in production. This method allows you to successfully isolate the code under test because it deals with known data, and this means you can make specific/testable assertions about the results of your database operations.

更新

仅仅因为它是一个非常有用的指南,如果你想提高可测试性,那么不在你的代码中做什么,我正在添加一个指向 Misko Hevery 的 如何编写 3v1L,不可测试的代码.它不特别涉及数据库测试,但它仍然很有帮助.祝测试愉快!

Just because it's an extraordinarily useful guide for what not to do in your code if you want to promote testability, I'm adding a link to Misko Hevery's How to Write 3v1L, Untestable Code. It's not involved with database testing in particular, but it's helpful nevertheless. Happy testing!

更新 2

我想回应关于推迟模型测试的评论,因为现有的代码库没有实现 PDO 用于数据库访问:

I wanted to respond to the comment about putting off model testing because the existing code base doesn't implement PDO for database access:

您的模型不必使用 PDO 来实现 PHPUnit 的 DbUnit 扩展.

如果您使用 PDO,它会让您的生活更轻松,但您不需要这样做.例如,假设您使用 PHP 的内置 pg_* PostgreSQL 函数构建了应用程序.PHPUnit 仍然允许您指定夹具,并且它仍然可以为每个测试重新构建它们——您只需在执行测试时将您的连接指向 DbUnit 扩展用于其夹具的同一资源.

It will make your life a bit easier if you use PDO, but you aren't required to do so. Say, for example, you've built your application with PHP's built-in pg_* PostgreSQL functions. PHPUnit still allows you to specify fixtures and it can still rebuild them for each test -- you would simply need to point your connection when performing tests to the same resource the DbUnit extension uses for its fixture.

相关文章