PHPUnitManual:8.4

From 흡혈양파의 번역工房
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.
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 (경합) 으로 인한 테스트 실패가 자주 발생할 것입니다.


Notes