PHPUnitManual:8.4
- 8.4 PHPUnit 데이터베이스 테스트 케이스의 설정
PHPUnit 데이터베이스 테스트 케이스의 설정
일반적으로, PHPUnit 을 사용하는 테스트 케이스는 PHPUnit_Framework_TestCase 클래스를 계승하여 다음과 같이 만들어집니다.
require_once "PHPUnit/Framework/TestCase.php";
class MyTest extends PHPUnit_Framework_TestCase
{
public function testCalculate()
{
$this->assertEquals(2, 1 + 1);
}
}
테스트 코드에서 Database Extension 을 사용할 경우에는 약간 더 복잡한데, 다른 추상 테스트 케이스를 계승하여야만 합니다. 그리고, 2개의 추상 메소드 getConnection() 과 getDataSet() 를 구현합니다.
require_once "PHPUnit/Extensions/Database/TestCase.php";
class MyGuestbookTest extends PHPUnit_Extensions_Database_TestCase
{
/**
* @return PHPUnit_Extensions_Database_DB_IDatabaseConnection
*/
public function getConnection()
{
$pdo = new PDO('sqlite::memory:');
return $this->createDefaultDBConnection($pdo, ':memory:');
}
/**
* @return PHPUnit_Extensions_Database_DataSet_IDataSet
*/
public function getDataSet()
{
return $this->createFlatXMLDataSet(dirname(__FILE__).'/_files/guestbook-seed.xml');
}
}
getConnection() 의 구현
clean-up와 fixture 로딩 기능을 사용하기 위해서는, PHPUnit Database Extension 에서 데이터베이스에 접속할 수 있어야 합니다. 데이터베이스 접속을 추상화하기 위해 PDO 라이브러리를 사용합니다. 중요한 것은, PHPUnit 의 Database Extension 을 사용하기 위해 어플리케이션을 PDO 베이스로 만들 필요는 없다는 것입니다. 이 기능을 사용하는 것은 단순히 clean-up 과 fixture 준비를 위한 것일 뿐입니다.
앞의 예에서는, in-memory Sqlite connection 을 만들어 createDefaultDBConnection 메소드에 넘겼습니다. 이 메소드는 PDO 의 인스턴스를 wrap 한 것으로, 2번째 인수 (데이터베이스 이름) 에 매우 간단한 데이터베이스 접속을 위한 추상화 레이를 넘깁니다. 이 인수의 type 은 PHPUnit_Extensions_Database_DB_IDatabaseConnection 입니다.
"데이터베이스 접속의 사용법" 에서 이 인터페이스의 API 와 활용법에 대해 설명할 것입니다.
getDataSet() 의 구현
getDataSet() 메소드를 정의하는 것은, 각각의 테스트를 실행하기 전의 데이터베이스의 초기 상태를 정의하는 것입니다. 데이터베이스의 상태를 추상화하기 위해 DataSet 과 DataTable 이라는 개념을 사용하여, 인터페이스 PHPUnit_Extensions_Database_DataSet_IDataSet 과 PHPUnit_Extensions_Database_DataSet_IDataTable 로 표현합니다. 다음 절에서는 이 개념의 작용과 이점을 자세히 설명할 것입니다.
구현하기 위해서 최소한으로 이해해야 하는 것은, getDataSet() 메소드는 setUp() 안에서 단 한 번 호출되고, fixture 의 dataset 을 취득하여 데이터베이스에 insert 한다는 것입니다. 앞의 예에서는, 팩토리 메소드 createFlatXMLDataSet($filename) 를 사용하여 XML 형식의 dataset 을 표현하였습니다.
Database Schema (DDL) 란?
PHPUnit 에서는, 테스트의 실행 전에 데이터베이스 스키마 (모든 테이블, 트리거, 시퀀스, 뷰 를 포함한다) 가 생성되어 있다고 가정합니다. 즉, 개발자는 test suite 를 실행하기 전에 데이터베이스를 올바르게 준비해야만 합니다.
데이터베이스 테스트의 사전 조건을 만족시키기 위해서는 다음과 같은 방법을 사용할 수 있습니다.
SQLite 메모리가 아닌 실체 데이터베이스를 사용할 경우, 처음에 phpMyAdmin for MySQL 등의 도구를 사용하여 데이터베이스를 준비한다면, 그 후에는 매 테스트의 실행 시에 재사용할 수 있습니다.
Doctrine 2 나 Propel 을 사용하는 경우, 해당 API 를 사용하여 테스트의 실행 전에 필요한 데이터베이스 스키마를 생성할 수 있습니다. PHPUnit 의 Bootstrap 기능을 사용하여 이 코드를 테스트 실행 전에 매번 실행할 수 있습니다.
Tip: 데이터베이스 테스트 케이스의 추상화
앞의 구현 예를 통해 보았듯이, getConnection() 메소드는 매우 정적인 메소드로 다양한 데이터베이스 테스트 케이스에서 재이용될 수 있습니다. 또한 테스트의 퍼포먼스를 떨어트리지 않고, 데이터베이스의 중복을 줄이기 위해 간단한 팩터링을 통해 범용적인 추상 테스트 케이스를 만들 수 있습니다. 이 방법으로도 테스트 케이스 별로 다른 데이터 fixture 를 지정할 수 있습니다.
require_once "PHPUnit/Extensions/Database/TestCase.php";
abstract class MyApp_Tests_DatabaseTestCase extends PHPUnit_Extensions_Database_TestCase
{
// PDO 인스턴스의 생성은, clean-up 과 fixture 로딩의 한 번 뿐
static private $pdo = null;
// PHPUnit_Extensions_Database_DB_IDatabaseConnection 인스턴스의 생성은 테스트 별로 한 번씩
private $conn = null;
final public function getConnection()
{
if ($this->conn === null) {
if (self::$pdo == null) {
self::$pdo = new PDO('sqlite::memory:');
}
$this->conn = $this->createDefaultDBConnection(self::$pdo, ':memory:');
}
return $this->conn;
}
}
여전히 데이터베이스 접속 정보를 PDO 접속 설정에 하드코딩한 상태입니다. PHPUnit 의 멋진 기능을 사용하여 테스트 케이스를 보다 범용으로 만들 수 있습니다. XML 설정 파일을 사용하면, 테스트 실행 별로 데이터베이스 접속을 설정할 수 있습니다. 먼저, "phpunit.xml" 파일을 어플리케이션의 tests/ 디렉토리에 다음 내용으로 작성합니다.
<?xml version="1.0" encoding="UTF-8" ?>
<phpunit>
<php>
<var name="DB_DSN" value="mysql:dbname=myguestbook;host=localhost" />
<var name="DB_USER" value="user" />
<var name="DB_PASSWD" value="passwd" />
<var name="DB_DBNAME" value="myguestbook" />
</php>
</phpunit>
테스트 케이스는 다음과 같이 수정합니다.
abstract class Generic_Tests_DatabaseTestCase extends PHPUnit_Extensions_Database_TestCase
{
// PDO 인스턴스의 생성은, clean-up 과 fixture 로딩의 한 번 뿐
static private $pdo = null;
// PHPUnit_Extensions_Database_DB_IDatabaseConnection 인스턴스의 생성은 테스트 별로 한 번씩
private $conn = null;
final public function getConnection()
{
if ($this->conn === null) {
if (self::$pdo == null) {
self::$pdo = new PDO( $GLOBALS['DB_DSN'], $GLOBALS['DB_USER'], $GLOBALS['DB_PASSWD'] );
}
$this->conn = $this->createDefaultDBConnection(self::$pdo, $GLOBALS['DB_DBNAME']);
}
return $this->conn;
}
}
데이터베이스의 설정 정보를 바꾸어 test suite 를 실행하는 방법은 다음과 같습니다.
user@desktop> phpunit --configuration developer-a.xml MyTests/
user@desktop> phpunit --configuration developer-b.xml MyTests/
데이터베이스 테스트를 실행할 때, 대상 테이터베이스를 변경할 수 있게 하는 것은 개발 머신에서 작업할 때 특히 중요합니다. 여러 개발자가 같은 데이터베이스 접속을 사용하여 데이터베이스 테스트를 실행한다면 race condition (경합) 으로 인한 테스트 실패가 자주 발생할 것입니다.