PHPUnit: How to test database interactions on remo

2019-04-09 13:57发布

问题:

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?

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.

回答1:

The short answer is to Read The Fine Manual entry on database testing at the PHPUnit manual.

And now the long answer ...

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.

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]he database is essentially a global input variable to your code

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.

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.

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

  1. Set up fixture
  2. Exercise System Under Test
  3. Verify outcome
  4. Teardown

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.

UPDATE

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!

UPDATE 2

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

Your models don't have to use PDO to implement PHPUnit's DbUnit extension.

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.