I think we can all agree that unit testing is important. Seriously, its a given.
I think the real hurdles for adoption of unit testing are installation of a testing framework, and wrapping your mind around the concepts of test composition and mocking/stubbing objects. The mere fact that installation is a pain stops most people out of the gate. Add in the fact that there are so few concrete examples of how test something like the PHP data objects like PDO. No wonder most people think of writing unit tests as a waste of time simply on
I use phpunit to create unit tests. I think this is the defacto standard when it comes to php development. Documentation is plentiful, and most proponents of unit testing have some experience using it. Installation can be done using PEAR. More information can be found on the phpunit site. Seriously, its that simple.
When looking to test a class using a PDO database connector I found this answer on StackOverflow to be extremely helpful, and I want to provide a concrete example.
The example MyClass constructor requires a PDO instance to be passed to it, and a method that returns a result set from a PDOStatement execution. Pretty simple class.
There is also a stub class called “MockPDO” that allows us to circumvent the native PHP PDO constructor, yet still mock the existing methods for further testing. The reason to do this is to get around a constructor error when mocking the PDO class. The unit test shows an example of creating mocks of both the extended PDO class, and a PDOStatement to be injected into the extended PDO class instance.
<?php
/**
* MyClass
*
* Example class to unit test. Constructor requires
* a valid PDO instance, only method getRecords returns
* result from example table.
*
* @package
* @version $id$
* @copyright
* @license
*/
class MyClass {
/**
* _dbh
*
* Class PDO instance
*
* @var mixed
* @access public
*/
public $_dbh;
/**
* __construct
*
* Constructor method that sets class $_dbh var
* to the provided PDO instance
*
* @param mixed $dbh
* @access public
* @return void
*/
public function __construct($dbh) {
$this->_dbh = $dbh;
}
/**
* getRecords
*
* Returns a result set from query
*
* @access public
* @return void
*/
public function getRecords() {
$sql = "SELECT * FROM myclass.records;";
$sth = $this->_dbh->prepare($sql);
$sth->execute();
return $sth->fetchAll();
}
}
/**
* MockPDO
*
* Mock PDO class to extend PDO, to prevent exceptions from
* internal PDO calls. Only to be used for Unit Testing.
*
* Use this every time you need to test the passing of a PDO instance
*
* @uses PDO
* @package
* @version $id$
* @copyright
* @license
*/
class MockPDO extends PDO {
/**
* __construct
*
* Empty constructor to prevent errors
*
* @access public
* @return void
*/
public function __construct() { }
}
/**
* MyClassTest
*
* @uses PHPUnit
* @uses _Framework_TestCase
* @package
* @version $id$
* @copyright
* @license
*/
class MyClassTest extends PHPUnit_Framework_TestCase {
/**
* testConstructor
*
* Test of the MyClass constructor. Only required
* class variable is a PDO instance
*
* @access public
* @return void
*/
public function testConstructor() {
// Create Mocked DB to pass to construtor
$mockdbh = $this->getMock("MockPDO", array(), array("mysql:dbname=myclass"));
$myclass = new MyClass($mockdbh);
// Test to make sure constructor returns a valid MyClass instance
$this->assertType("MyClass", $myclass);
}
/**
* testGetRecords
*
* Test the getRecords method of MyClass
*
* @access public
* @return void
*/
public function testGetRecords() {
// Create our mocked classes
$mockdbh = $this->getMock("MockPDO", array(), array("mysql:dbname=myclass"));
$mockstatement = $this->getMock("PDOStatement");
// Variable to store the Return Value of fetchAll
$records = array(
"foo",
"bar",
"baz",
);
// Expect/Return of mocked statement
$mockstatement->expects($this->any())
->method("fetchAll")
->will($this->returnValue($records));
// Expect/Return of mocked PDO
$mockdbh->expects($this->any())
->method("prepare")
->will($this->returnValue($mockstatement));
// Create an instance of our class
$myclass = new MyClass($mockdbh);
// Test to make sure getRecords method returns raw queried result set
$this->assertSame($records, $myclass->getRecords());
}
}
Post a Comment