views:

47

answers:

3

How would you test the following code with phpUnit?

class Product {

    protected $id = null;

    public function __construct($id) {
        $this->id = $id;
    }

    public function getDescription($language_id) {
        $db_query = mysql_query("SELECT * FROM products_description WHERE products_id = " . (int) $this->id . " AND language = " . (int) $language_id);
        return mysql_fetch_array($db_query);
    }

}


$product = new Product(1);
$product->getDescription(1); // Test this

At the moment I test it like this:

public function testShouldGetProductsDescription() {
    $product = new Product(1);
    $description = $product->getDescription(1);
    $expected_description = array(
        'name' => 'car',
        'short_text' => 'this is a car',
        'long_text' => 'longer description for the car'
    );
    $this->assertEquals($description, $expected_description);
}

But it doesn't seem to be a good solution for me. (This code is only a example and might not work correctly)

A: 

Your Product class is tightly coupled to the database, because it calls mysql_query directly. If you couple it loosely to the database, for example by using a Database object or a ProductStore object, you can test the Product class without actually using the database.

Sjoerd
The problem is I can't use a ORM because its part of a bigger project that was developed years ago. I want to test the new functions that I implement with tdd, but can't change all of the legacy code.
ipsum
+1  A: 

I've written test code like this before, and the thing that always makes me worry is that if the content of the database changes my test will fail even though the code is still correct.

There's a few solutions:

  1. Truncate your products table and insert the records needed for the test suite first. phpUnit has some tools which may help with this, see http://www.phpunit.de/manual/3.2/en/database.html for more info.
  2. If your Product object can also be used to save the product to the database you could test a round trip. i.e. create a new product with your product object. obtain the id of the newly created product. Then throw away the Product product object and create a new one to load it. This is not really a unit test as you're testing two distinct features of the object, but it does allow you to test without depending on existing data in the database or truncating the table.
  3. Extract your database access to a separate class when can be injected into Product at runtime. The database class can then be mocked for testing. This doesn't necessarily require you to use an ORM, but it does mean you'll need to change the classes interface, which may not be possible when testing legacy code.
Jim OHalloran
+2  A: 
Kayla Rose