Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to insert test case-specific database rows with PHPUnit

Tags:

php

phpunit

When using PHPUnit to test a class that relies on the database, the getDataSet() method provides data to serve as the test fixture for the entire suite of tests. That's great, but how does one provide data for a specific test? It's not unusual, especially when using minimized data sets, for each test to require different database data on top of the common data that all tests use. I know the PDO object is available, and in our case, the application's native DB object is also available (meaning, we can run raw queries or use other functionality in the app), but it would be nice to have a way to insert data that's driven off PHPUnit's DataSet containers so that all test data are handled in the same fashion for improved clarity and easier maintenance.

Is there some way to accomplish this?

like image 580
mr. w Avatar asked Feb 21 '12 21:02

mr. w


2 Answers

You could follow such dirty trick:

protected function getDataSet()
{
    if (in_array($this->getName(), array('testA', 'testB', '...'))) {
        return $this->createXMLDataSet(__DIR__ . '/_fixtures/fistureA.xml');
    }

    return $this->createXMLDataSet(__DIR__ . '/_fixtures/fixtureB.xml');
}

Small note: $this->getName() returns the current test method name

An alternative approach is to re-run set up operations in the begin of the test:

public function testA()
{
    $this->getDatabaseTester()->setDataSet($this->createFlatXMLDataSet(__DIR__ . '/_fixtures/fixtureForTestA.xml'));
    $this->getDatabaseTester()->onSetUp();

    /* your test code */
}
like image 134
zerkms Avatar answered Sep 30 '22 11:09

zerkms


It's a late answer but it could still be useful to some people I guess...

You can accomplish this by calling the execute method of a IDatabaseOperation which you can get from PHPUnit_Extensions_Database_Operation_Factory. You would basically use CLEAN_INSERT or INSERT.

As zerkms' second approach, you would call it at the beginning of each test that needs specific data. For instance:

public function testA() {
  PHPUnit_Extensions_Database_Operation_Factory::INSERT()
    ->execute($this->getConnection(), $this->createXMLDataSet(__DIR__.'/fixtureA.xml'));

  // Test code
}

However, the advantage of this solution is that the dataset of the whole test case remains unchanged, so:

  • Test methods remain independent from each other (whereas in zerkms' approach, if you don't / forget to specify a dataset in another test method, it would reuse the dataset from the previous (random?) method that changed the dataset, which is quite ugly and very error-prone IMHO). Here, any other test without such call at its beginning would just use the test case dataset as usual.
  • You can build your test dataset on top of the common (test case) dataset. If you use the INSERT operation (not CLEAN_INSERT), it will insert rows specific to the test after all rows from the common dataset. You could also use the DELETE operation to remove some unwanted rows from that common dataset.
  • BTW, not calling onSetUp() makes this work even if the set up operation of the test case has been changed.
like image 39
fbastien Avatar answered Sep 30 '22 12:09

fbastien