PHP Unit Database fixtures “the ruby way”

More or less everybody who does test driven development comes to the point where one realizes that the preparation of the database before each test is vital - for good results and for your mental health. If you don’t do this properly then you will find yourself at some day searching through hundreds ( or thousands ) of tests to find the one which screws the database so that subsequent tests fail - even tough they are developed perfect.

There are many ways how to solve this issue - let’s have a look:

- Manually

Each test inserts, update’s and delete’s data on his own. You have to agree in your team that the table’s are in the same state at the end of the test like it was before. I think this is the most common case - and I can tell you - DON’T DO THIS. It will lead you into troubles as long as you are not developing alone. There is always a “smartass” in your team who does not fully cleanup - or even worse delete contents which are expected to be in the table. You will have chaos - and you will end up searching for exactly these tests. No fun, much work - slows you down a lot. So - let’s skip this.

- Use DBUnit

Mike Lively Jr. ported the Java DbUnit Extension into PHPUnit. Really nice work. It does the job and is the right tool to be used. There is already a lot of information around concerning DBUnit - see Mike’s blog or Sebastian Bergmann’s Slides. As I already said - DBUnit is fine, doing the job. But this blog wouldn’t be called “Frontalaufprall” ( frontal impact ) if I would not try to do thing’s different ( == better ). And this is where it get’s interesting. I want to show you a more unusual way to prepare your fixtures - the “ruby” way.

Ok ok - we are all PHP evangelists - but still - not everything in Ruby is bad. There are a lot of things we can integrate and learn from. If you can’t beat them - join them :)

- Let’s have a closer look

Ruby uses YAML. YAML Ain’t Markup Language, it’s a readable, friendly language for storing lists, dictionaries, text, numerics and more.

And this is already the main difference to DBUnit where you usually write XML. This was the reason why we decided in our swoodoo Team to give YAML a chance - we were simply to lazy to deal with all these “<>” thingies.

For using YAML you need syck. Syck is a parser for YAML and cares about reading and writing. Syck is fast - the author claims

Do not disturb Syck because it is so focused on the task at hand that it will slay you mortally if you get in its way.

Actually I never benched it so I simply believe that it’s fast. Would be interesting to let it run vs. DbUnit one day - if I find time I will do this. ( very unlikely… )

As Symfony uses Syck - you can find really good installation description here and I can focus on the integration into PHPUnit.

Successful installed? Now we can talk php.

Each Testfile should have his own fixture file - best practise is that you create a “fixture” tree parallel to your test directory where you keep these files. The contents could look like this:

1:
APIKEY: ‘<?php echo $something ?>’
CUC: EZY
TRACKING_ID: 2
AFFILIATE_ID: 4545
MISC:

2:
APIKEY: ”
CUC: EXPD
TRACKING_ID: 3
AFFILIATE_ID: 45456
MISC:

Loading is done via

    public static function create($fileName)
    {
        $fileName = 'Fixtures'.DIRECTORY_SEPARATOR.$fileName;
        ob_start();
        include $fileName;
        $fileContents = ob_get_contents();
        ob_clean();
        $yamlData = syck_load($fileContents);
        return $yamlData;
    }

Do not be surprised about the freaky “loading” code - you can use “file_get_contents” if you want - but then you loose the possibilty to execute php code. And as you can see in my example sometimes it’s really good to dynamically create data.

Now we need to insert the data into our database.

    public static function load($fixtures, $tableName)
    {
        if (is_array($fixtures) &amp;&amp; count($fixtures)) {
            foreach ($fixtures as $fixture) {
                if (is_array($fixture) &amp;&amp; is_array(current($fixture))) {
                    Fixtures::load($fixture, $tableName);
                }
 
                $fields = array_keys($fixture);
                $statement = "INSERT INTO $tableName (" . implode(', ', $fields) . ") VALUES (:" . implode(", :", $fields) . ")";
                $stmt = self::$_db-&gt;prepare($statement);
                if (count($fixture)) {
                    foreach ($fixture as $key =&gt; $value ) {
                        $stmt-&gt;bindValue(':'.$key, $value);
                    }
                }
                $stmt-&gt;execute();
 
                self::$_usedTables[$tableName] = $tableName;            }
        }
    }

Where we assume that self::$_db is a valid PDO object - and self::$_usedTables is a simple array where we do store which tables we touched for proper rollback. More or less - this is it. In your setup Method of you test you load the data - and in tear down you reset it via:

if (!empty(self::$_usedTables)) {
            foreach (array_reverse(self::$_usedTables) as $tableName) {
                    self::$_db-&gt;execute("TRUNCATE TABLE $tableName");
            }
        }

All this should be put into a helper class - in our case we called it “Fixtures”. The loading and the resetting we did implement into a baseclass of all our tests - this means when we do write a test we can very comfortably load by calling:

$this->_fixtures = Fixtures::createAndLoad(’PageGeneratorTest’ . DIRECTORY_SEPARATOR . ‘updatepagegen.yml’, ‘page_generator’);

and this is it. Every Test starts with a fresh database and after each test it’s truncated again. No troubles with corrupted data. And it’s fast.

Note the ” $this->_fixtures = ” - by passing the loaded data back we can test against the fixture - not some static data. So when you change the fixture ( e.g. as you extend the tables ) you do not have to touch your tests:

$this->assertEquals( $this->_fixtures[4]['ID'], $ret[0]['id']);

That’s it. As I hopefully could show you - using YAML together with PHPUnit or any other Testtool works fine in PHP.

I see two main advantages of YAML over DBUnit:

- no hazzle with XML format
- fast, easy and lightweight

So if you decide for any reason not to go the “normal” way and use DBUnit - then you will be fine using YAML with syck. And if it’s just to cheer your team up that they can use at least some small parts from ruby ;)

P.S. I would be __VERY__ happy if someone could let me how I can avoid the translation of extended chars in the sourcecode examples ( & => & ) …. I did not find an easy way yet to avoid this.

Comments

4 Responses to “PHP Unit Database fixtures “the ruby way””

  1. René Leonhardt on May 8th, 2008 11:22 am

    Here is a shorter and more readable version of create():

    public static function &create($fileName) {
    ob_start();
    include ‘Fixtures/’.$fileName;
    $yamlData = syck_load(ob_get_clean());
    return $yamlData;
    }

  2. dodger on May 8th, 2008 11:24 am

    Thnx Rene :)

  3. Albo on May 15th, 2008 8:55 pm

    “how I can avoid the translation of extended chars in the sourcecode examples ”
    Using html_entity_decode()?
    http://php.net/html_entity_decode

  4. dodger on May 16th, 2008 9:26 am

    Albo,

    thnx it is a long time ago that I got such a nice “RTFM” :)

    Actually to precise my question - how can I do this without fixing the source of the WP plugin :)

Leave a Reply