php|tek TDD live code

By Jason. Filed in PHP  |   
Home this! Digg this! RSS 2.0 

Many thanks to everyone who attended my Test Driven Development tutorial today at php|tek in Chicago. As promised, here is the code we developed during the live coding sections of the tutorial.

The slides I presented are available from the php|tek site here.

Our first example was a simple Hash object. The requirements we developed for the hash were:
***Hash to repond to get(key) and set(key, value)
***Hash to have isValid(key)
***load from associative array at construction
access via object notation $hash->key or $hash->key = value
***access via array notation $hash[key] or $hash[key] = value

During our session, we covered 4 out of the 5 requirements using TDD.

Here was the test case we ended up with for our Hash object:


class HashTestCase extends BaseTestCase {
    function testClass() {

    function testGetAndSet() {
        $hash = new Hash;
        $this->assertMethodExists($hash, 'get');
        $this->assertMethodExists($hash, 'set');

        $hash->set('foo', 'bar');
        $this->assertEqual('bar', $hash->get('foo'));
        $hash->set('baz', 'blah');
        $this->assertEqual('blah', $hash->get('baz'));

    function testIsValid() {
        $hash = new Hash;
        $this->assertMethodExists($hash, 'isValid');

        $hash->set('foo', 'bar');

    function testLoadDuringConstruction() {
        $arr = array('foo' => 'bar');
        $hash = new Hash($arr);

        $this->assertEqual('bar', $hash->get('foo'));

        $hash2 = new Hash('false');
        $this->assertEqual('bar', $hash2->get('foo'));

    function testAccessAsArray() {
        $hash = new Hash(array('foo' => 'bar'));
        $this->assertEqual('bar', $hash['foo']);



The code we developed for the Hash class was:


class Hash implements ArrayAccess {
    function __construct($vals = false) {
        $this->vals = (is_array($vals)) ? $vals : array();
    protected $vals = array();
    function get($key) {
        return $this->vals[$key];
    function set($key, $value) {
        $this->vals[$key] = $value;
    function isValid($key) {
        return array_key_exists($key, $this->vals);
    // functions to implement ArrayAccess
    function offsetExists($key) {
        return $this->isValid($key);
    function offsetGet($key) {
        return $this->get($key);
    function offsetSet($key, $val) {
        $this->set($key, $val);
    function offsetUnset($key) {


After we reviewed more of the benefits of using TDD, we move on to a more meaty application: a guest book. Here was the test cases we developed:


class BaseTestCase extends UnitTestCase {
    function assertMethodExists($object, $method) {
        $this->assertTrue(method_exists($object, $method), get_class($object).' object has '.$method.' method');

class ModelTestCase extends BaseTestCase {
    function testClass() {
        $this->assertTrue(class_exists('Guestbook'), 'Guestbook class exists');

    function testSetDb() {
        $model = new Guestbook;
        $this->assertMethodExists($model, 'setDb');

    function testGetComments() {
        $model = new Guestbook;
        $this->assertMethodExists($model, 'getComments');

        $this->assertIsA($comments = $model->getComments(), 'array'
            , 'Guestbook::getComments() returns an array [%s]');
        $this->assertTrue(count($comments)>1, 'returned more than one comment');


    function testGetCommentQueryDatabaseWithOrderByClause() {
        $model = new Guestbook;
        $db = new Mockadodb_mysql;
        $db->expectOnce('getArray', array(new WantedPatternExpectation('/order\s+by\screated\s+desc\s*$/i')));

    function assertGreaterThan($val1, $val2) {
        $this->assertTrue($val1 > $val2, $val1.' is greater than '.$val2);

class DbTestCase extends BaseTestCase {
    function testDbConnection() {
        $this->assertIsA($db = DB::conn(), 'adodb_mysql');
        $this->assertMethodExists($db, 'getArray');
    function testAccessToGuestbookTable() {
        $db = DB::conn();
        $this->assertIsA($rs = $db->getArray('select * from Guestbook'), 'array');
        $this->assertTrue(count($rs) > 1, 'result contains more than one row');
        $expected_keys = array('name','comment','created');
        foreach(array_keys($rs[0]) as $key) {
            $this->assertTrue(in_array($key,$expected_keys), $key.' was not expected');

class PageTestCase extends WebTestCase {
    protected $url='http://gentoo/~sweatje/conf/phpt_tdd/live/guestbook.php';
    function testPage() {
        $this->assertNoUnwantedPattern('/fatal error/i');
        $this->assertTitle('Live Guestbook');

    function testInputForm() {

With these test cases, we developed:



require_once 'adodb/';

class DB {
        //static class, we do not need a constructor
        private function __construct() {}

        public static function conn() {
                static $conn;
                if (!$conn) {
                        $conn = adoNewConnection('mysql');
                        $conn->connect('localhost', 'phpauser', 'phpapass', 'phpa');
                return $conn;





getComments()  sorted by most recent first
addComment() - no blank comment, no SPAM


require_once 'db.php';

class Guestbook {
    protected $db;
    function __construct() {
        $this->db = DB::conn();
    function setDb($db) {
        $this->db = $db;
    function getComments() {
        return $this->db->getArray(
            from Guestbook
            order by created desc');





<h1>All my comments</h1>

< ?php

require_once 'model.php';

$guestbook = new Guestbook;
foreach($guestbook->getComments() as $comment) {
    echo '
<h2>', $comment['name'], '</h2>

', $comment['comment'], '';

<h1>Add your comment</h1>
<form method="post">
<input type="text" name="name"/>
<textarea name="comment"></textarea>



One Comment

  1. Comment by Dhaval:

    Hello All,

    can any one send me the url to download the eclipse-3.1 for PHP?

    Thanks in Advance,

Leave a Reply

Your email address will not be published.