Question

I am trying to use TDD on a class that manages database connections. However

  • I am often developing away from the network where the databases are available
  • I want to just test the classes not mess about with real connections, even to SQLite :memory:
  • I may want to test connections in a platform-independent manner (eg. exchanging PDO objects for MySQLi objects etc). Especially as the databases are not all MySQL, some are SQLServer.

Essentially I want to do this:

class ConnectionManager {
    ...
    public function getConnection($name) {
        $params = $this->lookup($name);
        return new \PDO($params['spec'], $params['username'], $params['password']);
    }
}

And in my test runner:

class ConnectionManagerTest extends \PHPUnit_Framework_TestCase {
    public function testGetConnection() {
        $cxn = new ConnectionManager(); 
        $this->assertNotNull($cxn->getConnection('test')); // or whatever
    }
}

Somehow I would like to use a mock of the PDO class. Is my only option to add an explicit parameter to the test class constructor or one of its methods? I've tried using 'Instance mocking' as per Mockery documentation, but as I'm using autoloading that results in 'fatal error cannot redeclare class' (duh).

I'd prefer not to pollute the contracts with code purely used in testing, but is that my only option?

Thanks for your help

Was it helpful?

Solution

This looks like a case where dependency injection could help you. There are many good articles on the web describing this technique (here is the wikipedia overview), and this previous stack overflow answer gives a concise explanation of the technique (the example is in Java, but it should explain the pattern and be transferable to another language easily enough).

Your point about 'not wanting to pollute the code with contracts purely used for testing' is an interesting one. What I think you will find, as you get more experience with TDD, is that writing code that is designed to be easily testable, and writing good clean code, are essentially two sides of the same coin. One of subtle powers of TDD (when practiced correctly) is that it leads you towards a good, clean design that can be easily grown with your application. By following an approach which puts testing at the heart of how you design your code, you are actually designing the code from a client's perspective, which leads to cleaner interfaces to different components in your code and ultimately makes the code that uses these interfaces cleaner.

Dependency injection is common technique applied when following TDD, and is a great illustration of these points. On one hand it is effective for testing because it allows you to easily swap components at test time, so that you can avoid having to call databases, mock and verify interactions between components and so on. However, it also supports generally good design because it leads you towards a low-coupled and flexible design (for example, if you are dependency injecting access to your database, it makes it fairly simple to change your data source from a database to some other technology).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top