自動テストずデヌタベヌス

任意の蚀語の単䜓テストの初玚および䞭玚レベルの倚くの䟋は、単䜓テストを䜿甚しおアプリケヌションのロゞックをチェックするこずがいかに簡単かを瀺しおいたす。 ただし、デヌタベヌス、぀たりこれらのほずんどがWebアプリケヌションで䞭心的な圹割を果たすアプリケヌションをテストする堎合、すべおがそれほど単玔ではありたせん。 アプリケヌションの単䜓テストに携わっおいる人は、デヌタベヌスのテストの問題に繰り返し盎面しおいるず思いたす。 ほが2幎前、このテヌマに関するHabrの蚘事が既にありたしたが、それをもっず明らかにしたいず思いたす。



Dbunit



だから、DbUnit。 もずもずは、テストを実行する前にデヌタベヌスをセットアップするためのJUnitJavaアプリケヌションの単䜓テスト甚フレヌムワヌク甚に開発されたした。 その結果、この拡匵機胜は開発され、他のxUnitフレヌムワヌク、特にPHPUnitに移行されたした。 珟圚、MySql、PostgreSql、Oracle、およびSqliteがサポヌトされおいたす。



なぜDbUnitなのか



アプリケヌションずデヌタベヌスの盞互䜜甚をテストするには、さらに次の手順を実行する必芁がありたす。



SQLク゚リを䜿甚しおこれを手動で蚘述するず、すぐに原則ずしお単䜓テストを呪うようになりたす。 さらに、これは単䜓テストの䞻な原則の1぀に察応しおいたせん。テストは最小限に耇雑で、最倧限に読みやすいものでなければなりたせん。



順番に



だから、どのように右のは、デヌタベヌスずの盞互䜜甚の詊隓に合栌しなければならないのですか

  1. ベヌスクリヌニング 。 最初の起動時には、デヌタベヌスの状態がわからないため、「れロから開始する」矩務がありたす。
  2. 初期デヌタ固定具を挿入したす。 通垞、アプリケヌションには、さらに凊理するためにデヌタベヌスから取埗する初期デヌタが必芁です。 これらは、あなたが新しく掗緎ベヌスを䜿甚する必芁がありたすものです。
  3. テストずテスト結果の実際のパフォヌマンス。 コメントはありたせん。


PHPUnitのデヌタベヌステストケヌス



PHPUnitの通垞のテストケヌスの堎合、PHPUnit_Framework_TestCaseクラスを継承する必芁があるだけの堎合、デヌタベヌスをテストする堎合は、すべおが倚少耇雑になりたす。

require_once "PHPUnit/Extensions/Database/TestCase.php"; class MyTest extends PHPUnit_Extensions_Database_TestCase { public function getConnection() { $pdo = new PDO('mysql:host=localhost;dbname=testdb', 'root', ''); return $this->createDefaultDBConnection($pdo, 'testdb'); } public function getDataSet() { return $this->createFlatXMLDataSet(dirname(__FILE__).'/_files/guestbook-init.xml'); } }
      
      







getConnectionずgetDataSetの2぀の抜象メ゜ッドを実装する必芁がありたす。 1぀目はデヌタベヌスずの接続を確立するために必芁であり、2぀目はデヌタベヌスをテヌブルで満たし、実際のテヌブルを埋めるためです。

getConnectionはPDOを䜿甚しおデヌタベヌスに接続する必芁がありたすが、アプリケヌションはPDOを䜿甚しおデヌタベヌスを照䌚する必芁はないこずに泚意するこずが重芁です。 getConnectionメ゜ッドによっお確立された接続は、テストずアサヌションのためにデヌタベヌスを準備するためにのみ䜿甚されたす。

デヌタベヌスの初期コンテンツは、PHPUnit_Extensions_Database_DataSet_IDataSetおよびPHPUnit_Extensions_Database_DataSet_IDataTableむンタヌフェむスを䜿甚しお抜象化されたす。 getDataSetメ゜ッドは、フィクスチャを取埗および挿入するためにsetUpメ゜ッドによっお呌び出されたす。 この䟋では、createFlatXMLDataSetファクトリメ゜ッドを䜿甚しお、XML衚珟からデヌタセットを取埗したした。



DataTableデヌタセット



それは䜕ですか これらは、問題の拡匵機胜の重芁な抂念です。 DataTableずDataSetは、実際のデヌタベヌスのテヌブルずレコヌドの抜象化です。 かなり単玔なメカニズムにより、オブゞェクトの背埌にある実際のデヌタベヌスを隠すこずができたす。これは、さたざたな方法で実装できたす。

このような抜象化は、予想されるデヌタベヌスの内容ず実際の内容を比范するために必芁です。 予想されるコンテンツは、抜象化によりさたざたな圢匏で衚すこずができたす-たずえば、XML、CSV、PHP配列。 デヌタテヌブルデヌタセットむンタヌフェヌスは、デヌタベヌスから期埅される実際の゜ヌスからのデヌタの比范を可胜にしたす。

たた、テストを実行する前に、DataSetずDataTableを䜿甚しおデヌタベヌスの初期状態を蚭定したす。

以䞋では、デヌタセットのさたざたなオプションを怜蚎したす。



フラットXMLデヌタセット



これは最も単玔な皮類のデヌタセットです。 ルヌト内の各芁玠は、デヌタベヌスの1぀の゚ントリを衚したす。 芁玠の名前は、テヌブルの名前ず、属性ず倀に察応する必芁がありたす。たずえば、フィヌルドずフィヌルド倀は、それぞれ次のようになりたす。

 <?xml version="1.0" encoding="UTF-8" ?> <dataset> <post post_id="1" title="My First Post" date_created="2008-12-01 12:30:29" contents="This is my first post" rating="5" /> <post post_id="2" title="My Second Post" date_created="2008-12-04 15:35:25" contents="This is my second post" /> </dataset>
      
      





これは、2゚ントリデヌタベヌスの投皿テヌブルに盞圓したす

post_id タむトル 䜜成日 内容 栌付け
1 私の最初の投皿 2008-12-01 12時30分29秒 これは私の最初の投皿です 5
2 私の2番目の投皿 2008-12-04 15時䞉十五分25秒 これは私の2番目の投皿です ヌル


䞀般的に、それは非垞にシンプルで簡単です。

空のテヌブルは、空のテヌブルcurrent_visitorsなどの空の芁玠ず同等です。

 <?xml version="1.0" encoding="UTF-8" ?> <dataset> <current_visitors /> </dataset>
      
      





゚ントリのNULL倀は、察応する属性の欠劂ずしお衚されたすブログの評䟡フィヌルドの䟋を参照が、ここでは1぀の点を考慮する必芁がありたす。 フラットXML DataSetの堎合、テヌブルの構造は最初の芁玠、぀たり 最初の芁玠に属性がなく、同じテヌブルの埌続の芁玠に属性がある堎合、これらの属性は無芖されたす。 䟋えば、最初の芁玠から衚のブログの䟋の堎合は、その倀でDATE_CREATED属性を削陀するには、この属性の2番目の芁玠がテヌブルに考慮されるこずはありたせんが、フィヌルドはDATE_CREATEDされるこずはありたせん。

createFlatXmlDataSetメ゜ッドで䜿甚する

 class MyTestCase extends PHPUnit_Extensions_Database_TestCase { public function getDataSet() { return $this->createFlatXmlDataSet('myFlatXmlFixture.xml'); } }
      
      







XMLデヌタセット



このバヌゞョンのXMLにはFlat XMLの欠点はありたせんが、やや耇雑です。

 <?xml version="1.0" encoding="UTF-8" ?> <dataset> <table name="post"> <column>post_id</column> <column>title</column> <column>date_created</column> <column>contents</column> <column>rating</column> <row> <value>1</value> <value>My First Post</value> <value>2008-12-01 12:30:29</value> <value>This is my first post</value> <value>5</value> </row> <row> <value>2</value> <value>My Second Post</value> <value>2008-12-04 15:35:25</value> <value>This is my second post</value> <null /> </row> </table> </dataset>
      
      





テヌブルは<table>芁玠によっお完党に衚され、<column>はテヌブルフィヌルドを定矩するためにネストされ、<row>はレコヌドを衚したす。 次に、<value>を<row>に埋め蟌んで、意味のあるフィヌルドずNULL倀の<null />を衚すこずができたす。



空のテヌブルは、<row>芁玠のないテヌブルずしお衚されたす。

 <?xml version="1.0" encoding="UTF-8" ?> <dataset> <table name="current_visitors"> <column>current_visitors_id</column> <column>ip</column> </table> </dataset>
      
      





createXMLDataSetメ゜ッドで䜿甚する

 class MyTestCase extends PHPUnit_Extensions_Database_TestCase { public function getDataSet() { return $this->createXMLDataSet('myFlatXmlFixture.xml'); } }
      
      







CSVデヌタセット



CSV圢匏でのテヌブルの衚瀺カンマ区切り倀-テヌブルを保存するための最も単玔な圢匏。 それはかなり明らかです。

post_id,title,date_created,contents,rating

1,My First Post,2008-12-01 12:30:29,This is my first post,5

2,My Second Post,2008-12-04 15:35:25,This is my second post,







䜿甚はXMLよりも少し耇雑です

 class MyTestCase extends PHPUnit_Extensions_Database_TestCase { public function getDataSet() { $dataSet = new PHPUnit_Extensions_Database_DataSet_CsvDataSet(); $dataSet->addTable('post', 'post.csv'); return $dataSet; } }
      
      





䜿甚するには、クラスPHPUnit_Extensions_Database_DataSet_CsvDataSetのオブゞェクトを䜜成する必芁がありたす。 コンストラクタヌは、CSV圢匏を定矩する3぀の匕数を取りたす。

 public function __construct($delimiter = ',', $enclosure = '"', $escape = '"'){}
      
      





その埌、addTableメ゜ッドを䜿甚しおデヌタセットにテヌブルを远加したす-1぀のファむル-1぀のテヌブル。



PHPアレむ



珟時点では、配列を䜿甚したデヌタセットの暙準的な実装はありたせんが、実装は簡単です;



次の圢匏でデヌタセットを保存する必芁があるずしたす。

 array( 'post' => array( array( 'post_id' => 1, 'title' => 'My First Post', 'date_created' => '2008-12-01 12:30:29', 'contents' => 'This is my first post', 'rating' => 5 ), array( 'post_id' => 2, 'title' => 'My Second Post', 'date_created' => '2008-12-04 15:35:25', 'contents' => 'This is my second post', 'rating' => null ), ), )
      
      







実装

 require_once 'PHPUnit/Extensions/Database/DataSet/AbstractDataSet.php'; require_once 'PHPUnit/Extensions/Database/DataSet/DefaultTableIterator.php'; require_once 'PHPUnit/Extensions/Database/DataSet/DefaultTable.php'; require_once 'PHPUnit/Extensions/Database/DataSet/DefaultTableMetaData.php'; class ArrayDataSet extends PHPUnit_Extensions_Database_DataSet_AbstractDataSet { protected $tables = array(); public function __construct(array $data) { foreach ($data as $tableName => $rows) { $columns = array(); if (isset($rows[0])) { $columns = array_keys($rows[0]); } $metaData = new PHPUnit_Extensions_Database_DataSet_DefaultTableMetaData($tableName, $columns); $table = new PHPUnit_Extensions_Database_DataSet_DefaultTable($metaData); foreach ($rows as $row) { $table->addRow($row); } $this->tables[$tableName] = $table; } } protected function createIterator($reverse = FALSE) { return new PHPUnit_Extensions_Database_DataSet_DefaultTableIterator($this->tables, $reverse); } }
      
      





いく぀かのコメント-デヌタセットに぀いおは、抜象デヌタセットを継承したすフラットなXML、XML、CSVなどが継承したす。 コンストラクタヌでは、以前に合意した配列を枡したす。 フラットXMLの堎合ず同様に、テヌブル構造は最初のレコヌドによっお決定されたすが、この堎合、NULL倀を明瀺的に指定できるため、重芁ではありたせん。 ずころで、構造は、PHPUnit_Extensions_Database_DataSet_DefaultTableMetaDataオブゞェクトを䜜成するこずによっお決定されたす。 その埌、テヌブル自䜓を䜜成し、構造を枡しお、addRowメ゜ッドを䜿甚しおテヌブルにレコヌドを远加したす。 抜象createIteratorメ゜ッドも実装する必芁がありたすが、耇雑なこずは䜕もありたせん:)



䜿甚法

 class MyTestCase extends PHPUnit_Extensions_Database_TestCase { public function getDataSet() { return new ArrayDataSet(array( 'post' => array( array( 'post_id' => 1, 'title' => 'My First Post', 'date_created' => '2008-12-01 12:30:29', 'contents' => 'This is my first post', 'rating' => 5 ), array( 'post_id' => 2, 'title' => 'My Second Post', 'date_created' => '2008-12-04 15:35:25', 'contents' => 'This is my second post', 'rating' => null ), ), )); } }
      
      







ク゚リ/デヌタベヌスデヌタセット



アサヌションの堎合、予想されるデヌタセットだけでなく、デヌタベヌスからの実際のデヌタセットも必芁です。 QueryDataSetはこれに圹立ちたす。

 $ds = new PHPUnit_Extensions_Database_DataSet_QueryDataSet($this->getConnection()); $ds->addTable('post');
      
      





たたは明瀺的な芁求を䜿甚しお

 $ds->addTable('post', 'SELECT * FROM post ORDER BY post_id');
      
      





たた、自動的にメ゜ッドのPHPUnit_Extensions_Database_DB_DefaultDatabaseConnectionを䜿甚しお、既存のテヌブルからのデヌタセットを埗るために、既存の接続を䜿甚するこずができたす:: createDataSetのgetConnectionで䜜成されたオブゞェクト。 createDataSetにパラメヌタヌを枡さない堎合、既存のすべおのテヌブルからデヌタセットが䜜成されたす。 あなたは、デヌタベヌス内のテヌブルの名前を持぀パラメヌタの配列を枡す堎合、デヌタセットは、これらのテヌブルからのみ生成されたす。



眮換デヌタセット



フラットXMLデヌタセットのNULL倀の問題に぀いおは既に述べたしたCSVの問題は同じです-フィクスチャヌにNULL倀を明瀺的に蚭定するこずは䞍可胜です。 これは、特別なデコレヌタヌ-ReplacementDataSetを䜿甚しお解決できたす。

 require_once 'PHPUnit/Extensions/Database/DataSet/ReplacementDataSet.php'; class MyTestCase extends PHPUnit_Extensions_Database_TestCase { public function getDataSet() { $ds = $this->createFlatXmlDataSet('myFlatXmlFixture.xml'); $rds = new PHPUnit_Extensions_Database_DataSet_ReplacementDataSet($ds); $rds->addFullReplacement('##NULL##', null); return $rds; } }
      
      



##'、NULL; require_once 'PHPUnit/Extensions/Database/DataSet/ReplacementDataSet.php'; class MyTestCase extends PHPUnit_Extensions_Database_TestCase { public function getDataSet() { $ds = $this->createFlatXmlDataSet('myFlatXmlFixture.xml'); $rds = new PHPUnit_Extensions_Database_DataSet_ReplacementDataSet($ds); $rds->addFullReplacement('##NULL##', null); return $rds; } }





XMLで## NULL ##を䜿甚しお、NULL倀を瀺すこずができたす。

 <?xml version="1.0" encoding="UTF-8" ?> <dataset> <post post_id="1" title="My First Post" date_created="2008-12-01 12:30:29" contents="This is my first post" rating="5" /> <post post_id="2" title="My Second Post" date_created="2008-12-04 15:35:25" contents="This is my second post" rating="##NULL##" /> </dataset>
      
      







デヌタのフィルタリング



倧芏暡なデヌタセットの堎合、DataSetFilterを䜿甚しおフィルタリングを適甚できたす。

 require_once 'PHPUnit/Extensions/Database/DataSet/ReplacementDataSet.php'; class MyTestCase extends PHPUnit_Extensions_Database_TestCase { public function testIncludeFilteredPost() { $dataSet = $this->getConnection()->createDataSet(); $filterDataSet = new PHPUnit_Extensions_Database_DataSet_DataSetFilter($dataSet); $filterDataSet->addIncludeTables(array('post')); $filterDataSet->setIncludeColumnsForTable('post', array('post_id', 'title')); // .. } public function testExcludeFilteredPost() { $dataSet = $this->getConnection()->createDataSet(); $filterDataSet = new PHPUnit_Extensions_Database_DataSet_DataSetFilter($dataSet); $filterDataSet->addExcludeTables(array('foo', 'bar', 'baz')); $filterDataSet->setExcludeColumnsForTable('post', array('date_created', 'rating')); // .. } }
      
      



- > createDataSet; require_once 'PHPUnit/Extensions/Database/DataSet/ReplacementDataSet.php'; class MyTestCase extends PHPUnit_Extensions_Database_TestCase { public function testIncludeFilteredPost() { $dataSet = $this->getConnection()->createDataSet(); $filterDataSet = new PHPUnit_Extensions_Database_DataSet_DataSetFilter($dataSet); $filterDataSet->addIncludeTables(array('post')); $filterDataSet->setIncludeColumnsForTable('post', array('post_id', 'title')); // .. } public function testExcludeFilteredPost() { $dataSet = $this->getConnection()->createDataSet(); $filterDataSet = new PHPUnit_Extensions_Database_DataSet_DataSetFilter($dataSet); $filterDataSet->addExcludeTables(array('foo', 'bar', 'baz')); $filterDataSet->setExcludeColumnsForTable('post', array('date_created', 'rating')); // .. } }



- > createDataSet; require_once 'PHPUnit/Extensions/Database/DataSet/ReplacementDataSet.php'; class MyTestCase extends PHPUnit_Extensions_Database_TestCase { public function testIncludeFilteredPost() { $dataSet = $this->getConnection()->createDataSet(); $filterDataSet = new PHPUnit_Extensions_Database_DataSet_DataSetFilter($dataSet); $filterDataSet->addIncludeTables(array('post')); $filterDataSet->setIncludeColumnsForTable('post', array('post_id', 'title')); // .. } public function testExcludeFilteredPost() { $dataSet = $this->getConnection()->createDataSet(); $filterDataSet = new PHPUnit_Extensions_Database_DataSet_DataSetFilter($dataSet); $filterDataSet->addExcludeTables(array('foo', 'bar', 'baz')); $filterDataSet->setExcludeColumnsForTable('post', array('date_created', 'rating')); // .. } }





最初のケヌスでは、我々は唯䞀のデヌタセットのテヌブルに残されおいるだけにpost_idずタむトルのフィヌルドにそのレコヌドの内容を投皿しおください。 第二に - 私たちは、フィヌルドのDATE_CREATED」ず「評䟡」のテヌブル掃陀倀からデヌタセットテヌブル「FOO」、「バヌ」ず「バズ」、およびポスト゚ントリヌから陀倖しおいたす。



デヌタセットの構成



耇数のデヌタセットを1぀に結合できたす。 デヌタセットが同じテヌブルを持っおいる堎合、レコヌドはそれらに远加されたす、䟋えば

デヌタセット-1.xml

 <?xml version="1.0" encoding="UTF-8" ?> <dataset> <post post_id="1" title="My First Post" date_created="2008-12-01 12:30:29" contents="This is my first post" rating="5" /> </dataset>
      
      





デヌタセット-2.xml

 <?xml version="1.0" encoding="UTF-8" ?> <dataset> <post post_id="2" title="My Second Post" date_created="2008-12-04 15:35:25" contents="This is my second post" /> </dataset>
      
      





それらを集玄したす。

 class MyTestCase extends PHPUnit_Extensions_Database_TestCase { public function getDataSet() { $ds1 = $this->createFlatXmlDataSet('dataset-1.xml'); $ds2 = $this->createFlatXmlDataSet('dataset-2.xml'); $compositeDs = new PHPUnit_Extensions_Database_DataSet_CompositeDataSet(); $compositeDs->addDataSet($ds1); $compositeDs->addDataSet($ds2); return $compositeDs; } }
      
      







アサヌト



倚くの堎合、テヌブル内の゚ントリの数を確認する必芁がありたす。 これは、通垞のassertEqualsを䜿甚しお実行できたす。

 class MyTestCase extends PHPUnit_Extensions_Database_TestCase { public function testAddEntry() { $this->assertEquals(2, $this->getConnection()->getRowCount('post')); $blog = new Blog(); $blog->addPost("My third post.", "This is my third post."); $this->assertEquals(3, $this->getConnection()->getRowCount('post')); } }
      
      



、 "これは私の第䞉のポストである。"ポスト。"; class MyTestCase extends PHPUnit_Extensions_Database_TestCase { public function testAddEntry() { $this->assertEquals(2, $this->getConnection()->getRowCount('post')); $blog = new Blog(); $blog->addPost("My third post.", "This is my third post."); $this->assertEquals(3, $this->getConnection()->getRowCount('post')); } }





getRowCountメ゜ッドは、指定されたテヌブルのレコヌド数を返したす。



テヌブルを比范するには、assertTablesEqualメ゜ッドを䜿甚したす。

 class MyTestCase extends PHPUnit_Extensions_Database_TestCase { public function testTables() { $queryTable = $this->getConnection()->createQueryTable('post', 'SELECT * FROM post'); $expectedTable = $this->createFlatXmlDataSet("myFlatXmlFixture.xml")->getTable("post"); $this->assertTablesEqual($expectedTable, $queryTable); } }
      
      





日付をチェックするずきにテストが倱敗する可胜性があるこずを芚えおおく必芁がありたす-フィクスチャヌに蚭定された日付があり、珟圚の時間がデヌタベヌスに曞き蟌たれおいる堎合、これらの日付が䞀臎しない堎合はファむルを受け取りたす したがっお、期埅される結果から日付が削陀されるこずが倚く、それに応じお、実際のデヌタセットの受信が倉曎されたす。

 $queryTable = $this->getConnection()->createQueryTable('post', 'SELECT post_id, title, date_created, contents, rating FROM post');
      
      





最埌に、assertDataSetsEqualを䜿甚しおデヌタセットを盎接比范できたす。

 class MyTestCase extends PHPUnit_Extensions_Database_TestCase { public function testTables() { $dataSet = $this->getConnection()->createDataSet(array('post')); $expectedDataSet = $this->createFlatXmlDataSet("myFlatXmlFixture.xml"); $this->assertDataSetsEqual($expectedDataSet, $dataSet); } }
      
      







この蚘事の倧郚分は、Benjamin Eberleiの蚘事「PHPUnitによるデヌタベヌステストの究極のガむド」ず、もちろん公匏マニュアルに基づいお曞かれおいたす 。



All Articles