在 Maghead ORM/DBAL (由於現在涵蓋的部分越來越廣不知道怎麼稱呼比較適當)的開發中,需要大量針對不同的資料庫做單元測試,因此在內部我們抽象化了一個 Maghead\Testing\ModelTestCase
的類別,讓同一套抽象邏輯可以抽換不同的資料庫連線來做整合的測試。
在 Travis CI 上,我們採用帶入不同的環境變數 DB 來換掉測試案例使用的 DataSource ID,如 DB=sqlite
, DB=mysql
… 等等,如 Build #1889
Travis CI 在執行完測試後,通過 coveralls 來傳送測試涵蓋率到 coveralls.io 平台上,但這邊會遇到的問題是,DB=sqlite
只會測試到所有 sqlite 相關的部分,而 DB=mysql
只會測試到 mysql 底層的部分,以至於測試涵蓋率沒有辦法加總統計,無法看見系統真正的弱點在哪。
這時候就需要擴充 PHPUnit TestSuite,在 TestSuite
迭代 TestCase
的時候帶入不同的環境變數,讓所有的測試得以在同一個 PHPUnit Process 底下運行。
首先我們需要擴充 TestCase 本身的函數,讓他可以直接注入環境變數
protected $currentDriverType;public function setCurrentDriverType($type)
{
$this->currentDriverType = $type;
}/**
* By overriding the DB environment variable, we can test specific test suites.
*/
public function getCurrentDriverType()
{
// self::DEFAULT_DRIVER_TYPE
return $this->currentDriverType ?: getenv('DB') ?: self::DEFAULT_DRIVER_TYPE;
}
接著來實作一個 Abstract TestSuite class (注意,必須為 abstract class ,否則 PHPUnit 會將他實例化運行)
use PHPUnit\Framework\TestSuite;abstract class AbstractDatabaseTestSuite extends TestSuite
{
static $crossPlatformTests = [
'AuthorBooks\\Tests\\AuthorTest',
'AuthorBooks\\Tests\\AuthorAddressTest',
'AuthorBooks\\Tests\\BookTest',
'AuthorBooks\\Tests\\AuthorBookTest',
'AuthorBooks\\Tests\\AuthorCollectionTest',
'PageApp\\Tests\\PageTest',
];public static function registerTests(TestSuite $suite)
{
foreach (static::$crossPlatformTests as $testCase) {
if (!class_exists($testCase, true)) {
throw new Exception("$testCase doesn't exist.");
}
$suite->addTestSuite($testCase);
}
}public function setTestingDriverType($type)
{
foreach ($this->tests() as $ts) {
foreach ($ts->tests() as $tc) {
if (method_exists($tc, 'setCurrentDriverType')) {
$tc->setCurrentDriverType($type);
}
}
}
}
}
你在上面可以看到第一層迭代 tests() 回傳的是所有的 TestSuite,第二層 tests() 回傳的是所有 TestSuite 中的 TestCase。
接著我們只需要針對不同的 Database Driver 實作 Suite:
class PgsqlSuiteTest extends AbstractDatabaseTestSuite
{
/**
* @requires extension pgsql
*/
public static function suite()
{
$suite = new self;
$suite->registerTests($suite);
$suite->setTestingDriverType('pgsql');
return $suite;
}
}class MysqlSuiteTest extends AbstractDatabaseTestSuite
{
/**
* @requires extension mysql
*/
public static function suite()
{
$suite = new self;
$suite->registerTests($suite);
$suite->setTestingDriverType('mysql');
return $suite;
}
}class SqliteSuiteTest extends AbstractDatabaseTestSuite
{
/**
* @requires extension sqlite
*/
public static function suite()
{
$suite = new self;
$suite->registerTests($suite);
$suite->setTestingDriverType('sqlite');
return $suite;
}
}
最後,測試一下:
phpunit tests/CrossPlatformSuiteTest.php
打完收工。