Sharing Fixture Between Tests
There are few good reasons to share fixtures between tests, but in most cases the need to share a fixture between tests stems from an unresolved design problem.
A good example of a fixture that makes sense to share across several tests is a database connection: you log into the database once and reuse the database connection instead of creating a new connection for each test. This makes your tests run faster.
PHPUnit 3.5 removes the fixture sharing feature of the TestSuite class. It was tedious to use this feature as it required the usage of a custom TestSuite class in addition to the test case class. Furthermore, its implementation was a "hack".
PHPUnit 3.4 introduced the setUpBeforeClass() and tearDownAfterClass() template methods that can be used in a test case class. These methods allow a much cleaner and simpler implementation of fixture sharing between tests of the same test case class:
<?php
class DatabaseTest extends PHPUnit_Framework_TestCase
{
protected static $dbh;
public static function setUpBeforeClass()
{
self::$dbh = new PDO('sqlite::memory:');
}
public static function tearDownAfterClass()
{
self::$dbh = NULL;
}
}
?>
The example above uses the setUpBeforeClass() and tearDownAfterClass() template methods to connect to the database before the test case class' first test and to disconnect from the database after the last test of the test case, respectively.
It cannot be emphasized enough that sharing fixtures between tests reduces the value of the tests. The underlying design problem is that objects are not loosely coupled. You will achieve better results solving the underlying design problem and then writing tests using test doubles, than by creating dependencies between tests at runtime and ignoring the opportunity to improve your design.
14/02/2010 at 18:39 Permalink
Same applies to fixture creation: always use a separate connection to create/drop the fixture database, tables and data.
New queries will obviously update everything ie previous state will only be factor if the test makes an assertion before a new query is performed with the shared connection. In practice the risk is low but it does exist. Data is precious and anything to do with data storage ought to go the extra mile.
Reply
10/03/2010 at 17:46 Permalink
From a purely theoretical standpoint, you are absolutely correct. Realistically speaking though, MySQL, SQLite and and maybe Postgres database platforms you can target to have your tests run in a timely fashion. Some database platforms simply do not like creating connections (they are very slow at doing this), or executing DDL queries. Particularly, when running our tests (ZF) against Db2, Oracle, MSSQL- these tests take anywhere between 5mins and 20mins. Db2 on i5 takes around 4 hours.
Since we have to test our Db abstraction layer against various databases platforms, it makes sense to craft them in such a way that makes running them as painless as possible. If that means sharing the connection, having to manually reset state, clear tables, and so on for each test, then we must do it because it's better to do that than have tests take too long to run to be useful.
-ralph
Reply
07/05/2010 at 16:15 Permalink
Can you please tell me if you ever used PHPUnit with Maven for PHP? It seems there is a problem with that when using versions newer than 3.3.9. Is there anything I can do to use the latest version with Maven for PHP? I NEED that setUpBeforeClass() and tearDownAfterClass() and they are not in the 3.3.9 version.
Thank you!
Reply
07/05/2010 at 16:18 Permalink
Reply
11/05/2010 at 12:08 Permalink
Reply
11/05/2010 at 12:48 Permalink
Maybe you can help me with a suggestion for my problem here?
http://stackoverflow.com/questions/2773744/phpunit-maven-for-php-setupbeforeclass-not-called
It's in fact the problem I described here but maybe you can give me a hint from the stacktrace? http://pastie.org/948377
Thanks in advance!
Reply