PHPUnitManual:8.4

From 흡혈양파의 번역工房
Revision as of 08:22, 2 July 2013 by Onionmixer (talk | contribs) (PHPUnit 8.4 PHPUnit 데이터베이스 테스트 케이스의 설정 페이지 추가)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search
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