स्वचालित परीक्षण और डेटाबेस

किसी भी भाषा में इकाई परीक्षण के प्राथमिक और मध्यवर्ती स्तर के कई उदाहरण बताते हैं कि इकाई परीक्षणों का उपयोग करके अपने अनुप्रयोगों के तर्क की जांच करना कितना सरल है। हालाँकि, अनुप्रयोगों के परीक्षण के दौरान सब कुछ इतना सरल नहीं होता है जिसमें डेटाबेस द्वारा केंद्रीय भूमिका निभाई जाती है, अर्थात वेब अनुप्रयोगों में इनमें से अधिकांश। जो लोग अपने अनुप्रयोगों के यूनिट परीक्षण में लगे हुए हैं, मुझे लगता है कि डेटाबेस के परीक्षण की समस्या का बार-बार सामना करना पड़ा है। लगभग 2 साल पहले इस विषय पर पहले से ही हैबर पर एक लेख था , लेकिन मैं इसे और अधिक प्रकट करना चाहूंगा।

DbUnit


तो, DbUnit। इसे मूल रूप से JUnit (इकाई परीक्षण के लिए एक फ्रेमवर्क जावा अनुप्रयोगों के लिए) के लिए विकसित किया गया था ताकि परीक्षण चलाने से पहले एक डेटाबेस स्थापित किया जा सके। नतीजतन, विस्तार विशेष रूप से PHPUnit के लिए अन्य xUnit रूपरेखाओं में विकसित और स्थानांतरित हो गया। MySql, PostgreSql, Oracle, और Sqlite वर्तमान में समर्थित हैं।

DbUnit क्यों?


डेटाबेस के साथ अपने आवेदन की बातचीत का परीक्षण करने के लिए, आपको निम्न चरणों को अतिरिक्त रूप से करने की आवश्यकता है:

यदि आप इसे SQL प्रश्नों का उपयोग करके मैन्युअल रूप से लिखते हैं, तो बहुत जल्द आप सिद्धांत रूप में इकाई परीक्षण को शाप देना शुरू करते हैं। इसके अलावा, यह इकाई परीक्षण के मुख्य सिद्धांतों में से एक के अनुरूप नहीं है - परीक्षण न्यूनतम रूप से जटिल और अधिकतम पठनीय होना चाहिए।

क्रम में


तो, डेटाबेस के साथ बातचीत का परीक्षण कैसे सही ढंग से पास होना चाहिए?
  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 ()। पहला डेटाबेस के साथ कनेक्शन स्थापित करने के लिए आवश्यक है, दूसरा डेटाबेस को तालिकाओं के साथ भरने और वास्तविक तालिकाओं को भरने के लिए।
यह ध्यान रखना महत्वपूर्ण है कि getConnection () डेटाबेस से कनेक्ट करने के लिए PDO का उपयोग करना चाहिए, लेकिन डेटाबेस को क्वेरी करने के लिए आपके एप्लिकेशन को PDO का उपयोग करने की आवश्यकता नहीं है। गेटकनेक्शन () विधि द्वारा स्थापित कनेक्शन का उपयोग केवल परीक्षण और अभिकथन के लिए डेटाबेस तैयार करने के लिए किया जाता है।
डेटाबेस की प्रारंभिक सामग्री को इंटरफेस PHPUnit_Extensions_Database_DataSet_IDataSet और PHPUnit_Extensions_Database_DataSet_IDITTable का उपयोग करके अमूर्त किया जाता है। GetDataSet () विधि को जुड़नार प्राप्त करने और सम्मिलित करने के लिए सेटअप () विधि द्वारा कहा जाता है। उदाहरण में, हमने XML प्रतिनिधित्व से डेटासेट प्राप्त करने के लिए createFlatXMLDataSet () फ़ैक्टरी विधि का उपयोग किया।

डेटाटेबल्स और डेटासेट्स


तो क्या है? ये प्रश्न में विस्तार की प्रमुख अवधारणाएँ हैं। DataTable और DataSet एक वास्तविक डेटाबेस में टेबल और रिकॉर्ड के लिए एक अमूर्त है। एक साधारण प्रणाली आपको वस्तुओं के पीछे वास्तविक डेटाबेस को छिपाने की अनुमति देती है, जिसे बदले में विभिन्न तरीकों से लागू किया जा सकता है।
डेटाबेस और वास्तविक एक की अपेक्षित सामग्री की तुलना करने के लिए ऐसा अमूर्त आवश्यक है। अमूर्त के कारण अपेक्षित सामग्री को विभिन्न रूपों में दर्शाया जा सकता है - उदाहरण के लिए, एक्सएमएल, सीएसवी, पीएचपी सरणियाँ। डेटाटेबल और डेटासेट्स इंटरफेस आपको डेटाबेस से वास्तविक स्रोत से अपेक्षित डेटा की तुलना करने की अनुमति देते हैं।
इसके अलावा, डेटासेट और डेटाटेबल का उपयोग परीक्षण चलाने से पहले डेटाबेस की प्रारंभिक स्थिति को सेट करने के लिए किया जाता है।
नीचे हम डेटासेट के लिए विभिन्न विकल्पों पर विचार करते हैं।

फ्लैट 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" /> <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शीर्षकdate_createdअंतर्वस्तुरेटिंग
1मेरी पहली पोस्ट2008-12-01 12:30:29यह मेरी पहली पोस्ट है5
2मेरी दूसरी पोस्ट2008-12-04 15:35:25यह मेरी दूसरी पोस्ट हैशून्य

सामान्य तौर पर, यह बहुत सरल और सीधा है।
एक खाली तालिका एक खाली तत्व के बराबर है, उदाहरण के लिए, एक खाली तालिका current_visitors:
 <?xml version="1.0" encoding="UTF-8" ?> <dataset> <current_visitors /> </dataset> 

प्रविष्टि के लिए पूर्ण मानों को संबंधित विशेषता की अनुपस्थिति के रूप में दर्शाया जाता है (ब्लॉग, रेटिंग फ़ील्ड के साथ उदाहरण देखें), हालांकि, एक बिंदु को यहां ध्यान में रखा जाना चाहिए। फ्लैट एक्सएमएल डेटासेट के लिए, तालिका की संरचना पहले तत्व द्वारा निर्धारित की जाती है, अर्थात। यदि पहले तत्व में कोई विशेषता नहीं है, और उसी तालिका के लिए बाद के तत्वों में वे हैं, तो इन विशेषताओं को अनदेखा किया जाएगा। उदाहरण के लिए, यदि ब्लॉग टेबल के साथ उदाहरण में date_created विशेषता के साथ इसके मूल्य को पहले तत्व से हटा दिया जाता है, तो यह विशेषता दूसरे तत्व को ध्यान में नहीं रखी जाएगी और date_created फ़ील्ड तालिका में नहीं होगी।
CreateFlatXmlDataSet () विधि के साथ प्रयोग:
 class MyTestCase extends PHPUnit_Extensions_Database_TestCase { public function getDataSet() { return $this->createFlatXmlDataSet('myFlatXmlFixture.xml'); } } 


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> 

एक तालिका को पूरी तरह से एक <तालिका> तत्व द्वारा दर्शाया जाता है, जिसमें <कॉलम> टेबल फ़ील्ड को परिभाषित करने के लिए नेस्टेड हैं और रिकॉर्ड को दर्शाने के लिए <पंक्ति>। बदले में, <मान> अर्थपूर्ण क्षेत्रों का प्रतिनिधित्व करने के लिए <n> में पंक्तिबद्ध किया जा सकता है और NULL मूल्यों के लिए <null />।

एक खाली तालिका को <पंक्ति> तत्वों के बिना तालिका के रूप में दर्शाया गया है:
 <?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 प्रारूप को परिभाषित करता है:
 public function __construct($delimiter = ',', $enclosure = '"', $escape = '"'){} 

उसके बाद, AddTable मेथड - वन फाइल - वन टेबल का उपयोग करके टेबल्स को डेटासेट में जोड़ें।

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 और अन्य से विरासत में मिलते हैं) प्राप्त करते हैं। कंस्ट्रक्टर में, हम पहले से सहमत सरणी को पास करते हैं। जैसा कि फ्लैट एक्सएमएल के मामले में, तालिका संरचना पहले रिकॉर्ड द्वारा निर्धारित की जाती है, लेकिन इस मामले में यह महत्वपूर्ण नहीं है, क्योंकि हम स्पष्ट रूप से 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 मान सेट करना असंभव है)। यह एक विशेष डेकोरेटर का उपयोग करके हल किया जा सकता है - रिप्लेसमेंटडैटसेट:
 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> 


डेटा फ़िल्टरिंग


बड़े डेटासेट के मामले में, आप डेटासेटफिल्टर का उपयोग करके फ़िल्टरिंग लागू कर सकते हैं:
 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')); // .. } } 

पहले मामले में, हमने डेटासेट में केवल पोस्ट टेबल और उसके रिकॉर्ड की सामग्री को केवल पोस्ट_आईडी और शीर्षक फ़ील्ड के लिए छोड़ दिया है। दूसरे में, हमने डेटासेट से तालिकाओं 'फू', 'बार' और 'बाज' को बाहर कर दिया, और पोस्ट टेबल प्रविष्टियों से 'date_created' और 'रेटिंग' फ़ील्ड के मान हटा दिए।

डेटासेट की संरचना


हम कई डेटासेट को एक में जोड़ सकते हैं। यदि डेटासेट में समान टेबल हैं, तो रिकॉर्ड्स को उनके साथ जोड़ा जाएगा, उदाहरण के लिए:
डाटासेट-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; } } 


इस बात पर ज़ोर


किसी तालिका में प्रविष्टियों की संख्या की जांच करना अक्सर आवश्यक होता है। इसे नियमित रूप से उपयोग करके किया जा सकता है:
 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'); 

अंत में, आप सीधे डेटासेट की तुलना asDDataSetsEqual () से कर सकते हैं:
 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); } } 


लेख काफी हद तक बेंजामिन एबरेली के लेख "द अल्टीमेट गाइड टू डेटाबेस-टेस्टिंग विथ PHPUnit" और निश्चित रूप से आधिकारिक मैनुअल के आधार पर लिखा गया है।

Source: https://habr.com/ru/post/In113872/


All Articles