6

नकली मैंने कुछ साल पहले ज़ेन फ्रेमवर्क को this tutorial के बाद सीखना शुरू किया था। वहां, यह दिखाता है कि डेटाबेस कनेक्शन प्राप्त करने के लिए Zend\Db\Adapter\Adapter कक्षा का उपयोग करके मैपर बनाए जाते हैं, और इस तरह मैंने बिना किसी समस्या के डेटाबेस के साथ काम किया है।ज़ेंड फ्रेमवर्क 2 और PHPUnit - ज़ेंड डीबी एडाप्टर एडाप्टर क्लास

अब मैं सीखने की कोशिश कर रहा हूं कि ज़ेंड अनुप्रयोगों पर PHPUnit का उपयोग कैसे करें, और मैपर में फ़ंक्शंस का परीक्षण करने में कठिनाइयों में भाग ले रहा हूं क्योंकि मैं Zend\Db\Adapter\Adapter कक्षा का नकल करने में असमर्थ हूं।

This tutorial ज़ेंड वेबसाइट पर डेटाबेस कनेक्शन का मज़ाक उड़ाता है, लेकिन यह Zend\Db\TableGateway\TableGateway कक्षा का उपयोग करता है। अन्य सभी ट्यूटोरियल मैं ऑनलाइन पाया है Zend\Db\Adapter\Adapter वर्ग के बारे में भी इस वर्ग का उपयोग करें, और केवल एक चीज मैंने पाया this है:

$date = new DateTime(); 
$mockStatement = $this->getMock('Zend\Db\Adapter\Driver\Pdo\Statement'); 
$mockStatement->expects($this->once())->method('execute')->with($this->equalTo(array(
    'timestamp' => $date->format(FormatterInterface::DEFAULT_DATETIME_FORMAT) 
))); 

$mockDbDriver = $this->getMockBuilder('Zend\Db\Adapter\Driver\Pdo\Pdo') 
    ->disableOriginalConstructor() 
    ->getMock(); 

$mockDbAdapter = $this->getMock('Zend\Db\Adapter\Adapter', array(), array($mockDbDriver)); 
$mockDbAdapter->expects($this->once()) 
    ->method('query') 
    ->will($this->returnValue($mockStatement)); 

मैं अपने setUp विधि में है कि डाल लेकिन पर phpunit चल कोशिश की है

Fatal error: Call to a member function createStatement() on null in C:\Program Files (x86)\Zend\Apache2\htdocs\test_project\vendor\zendframework\zend-db\src\Sql\Sql.php on line 128

तो मेरे सवाल का, तुम कैसे PHPUnit में Zend\Db\Adapter\Adapter वर्ग नकली करते है: परीक्षण वर्ग मुझे निम्न त्रुटि देता है?

मैंने देखा है this question जो समान है, लेकिन Zend/Db/Adapter/AdapterInterface इसके बजाय, और मैं उस स्थिति को अपनी स्थिति में अनुवादित नहीं कर सकता। नीचे मैपर और टेस्ट क्लास कोड।

ProductMapper.php:

public function __construct(Adapter $dbAdapter) { 
    $this->dbAdapter = $dbAdapter; 
    $this->sql = new Sql($dbAdapter); 
} 

public function fetchAllProducts() { 
    $select = $this->sql->select('products'); 

    $statement = $this->sql->prepareStatementForSqlObject($select); 
    $results = $statement->execute(); 

    $hydrator = new ClassMethods(); 
    $product = new ProductEntity(); 
    $resultset = new HydratingResultSet($hydrator, $product); 
    $resultset->initialize($results); 
    $resultset->buffer(); 

    return $resultset; 
} 

ProductMapperTest.php:

public function setUp() { 
    $date = new DateTime(); 

    $mockStatement = $this->getMock('Zend\Db\Adapter\Driver\Pdo\Statement'); 
    $mockStatement->expects($this->once())->method('execute')->with($this->equalTo(array(
      'timestamp' => $date->format(FormatterInterface::DEFAULT_DATETIME_FORMAT) 
    ))); 

    $mockDbDriver = $this->getMockBuilder('Zend\Db\Adapter\Driver\Pdo\Pdo')->disableOriginalConstructor()->getMock(); 

    $this->mockDbAdapter = $this->getMock('Zend\Db\Adapter\Adapter', array(), array(
      $mockDbDriver 
    )); 
    $this->mockDbAdapter->expects($this->once())->method('query')->will($this->returnValue($mockStatement)); 
} 

public function testFetchAllProducts() { 
    $resultsSet = new ResultSet(); 

    $productMapper = new ProductMapper($this->mockDbAdapter); 

    $this->assertSame($resultsSet, $productMapper->fetchAllProducts()); 
} 

संपादित करें # 1:

का पालन करें विल्ट के जवाब से पर ing, मैं निर्माता में Sql वर्ग का उपयोग करने के लिए अपने नक्शाकार बदल गया है और करने के लिए अपने टेस्ट वर्ग बदल दिया है:

public function setUp() { 
    $mockSelect = $this->getMock('Zend\Db\Sql\Select'); 

    $mockDbAdapter = $this->getMockBuilder('Zend\Db\Adapter\AdapterInterface')->disableOriginalConstructor()->getMock(); 

    $this->mockStatement = $this->getMock('Zend\Db\Adapter\Driver\Pdo\Statement'); 

    $this->mockSql = $this->getMock('Zend\Db\Sql\Sql', array('select', 'prepareStatementForSqlObject'), array($mockDbAdapter)); 
    $this->mockSql->method('select')->will($this->returnValue($mockSelect)); 
    $this->mockSql->method('prepareStatementForSqlObject')->will($this->returnValue($this->mockStatement)); 
} 

public function testFetchAllProducts() { 
    $resultsSet = new ResultSet(); 

    $this->mockStatement->expects($this->once())->method('execute')->with()->will($this->returnValue($resultsSet)); 

    $productMapper = new ProductMapper($this->mockSql); 

    $this->assertSame($resultsSet, $productMapper->fetchAllProducts()); 
} 

हालांकि, अब मैं निम्नलिखित त्रुटि मिलती है:

ProductTest\Model\ProductMapperTest::testFetchAllProducts Failed asserting that two variables reference the same object.

है कौन सा $this->assertSame($resultsSet, $productMapper->fetchAllProducts()); लाइन से आ रहा है। क्या मैंने कुछ गलत तरीके से मजाक किया है?


# 2 संपादित करें:

विल्ट ने सुझाव दिया है, मैं परीक्षण वर्ग एक StatementInterface का उपयोग करने के बजाय एक बयान उपहास करने के लिए, बदला तो कोड अब लगता है कि:

public function setUp() { 

    $mockSelect = $this->getMock('Zend\Db\Sql\Select'); 

    $mockDbAdapter = $this->getMockBuilder('Zend\Db\Adapter\AdapterInterface')->disableOriginalConstructor()->getMock(); 

    $this->mockStatement = $this->getMock('Zend\Db\Adapter\Driver\StatementInterface'); 

    $this->mockSql = $this->getMock('Zend\Db\Sql\Sql', array('select', 'prepareStatementForSqlObject'), array($mockDbAdapter)); 
    $this->mockSql->method('select')->will($this->returnValue($mockSelect)); 
    $this->mockSql->method('prepareStatementForSqlObject')->will($this->returnValue($this->mockStatement)); 

} 

public function testFetchAllProducts() { 
    $resultsSet = new ResultSet(); 

    $this->mockStatement->expects($this->once())->method('execute')->with()->will($this->returnValue($resultsSet)); 

    $productMapper = new ProductMapper($this->mockSql); 

    $this->assertSame($resultsSet, $productMapper->fetchAllProducts()); 
} 

लेकिन परीक्षण केस अभी भी उपरोक्त के रूप में विफल रहा है। मैंने कोड की रेखा को नहीं बदला है जो execute विधि का मज़ाक उड़ा रहा है क्योंकि मुझे लगता है कि यह पहले से ही $resultsSet लौटा रहा है, हालांकि मैं गलत हो सकता था!

उत्तर

3

शायद __construct विधि को Sql उदाहरण को एक तर्क के रूप में बदलने के लिए बेहतर होगा। ऐसा लगता है कि $dbAdapter केवल कन्स्ट्रक्टर के अंदर उपयोग किया जाता है और इसके कारण मुझे लगता है कि आपके ProductMapper वर्ग की वास्तविक निर्भरता Adapter उदाहरण नहीं है, बल्कि Sql उदाहरण है। यदि आप वह परिवर्तन करते हैं तो आपको केवल ProductMapperTest के अंदर Sql कक्षा का नकल करने की आवश्यकता है।

आप अपने कोड के अंदर इस तरह के एक परिवर्तन करने के लिए नहीं करना चाहते हैं और आप अभी भी वर्तमान ProductMapper वर्ग तुम भी है कि Sql वर्ग बुला रहा है Adapter वर्ग के सभी अन्य तरीकों नकली चाहिए के लिए एक परीक्षण लेखन जारी रखना चाहते हैं आंतरिक रूप से।

अभी आप अपने Sql उदाहरण है, जिसमें आंतरिक Adapter वर्ग के createStatement प्रणाली को बुलाती है पर $this->sql->prepareStatementForSqlObject($select); फोन (आपको लगता है कि here on line 128 inside the Sql class देख सकते हैं)। लेकिन आपके मामले में Adapter एक नकली है और यही कारण त्रुटि फेंक दिया जाता है है:

Fatal error: Call to a member function createStatement() on null in C:\Program Files (x86)\Zend\Apache2\htdocs\test_project\vendor\zendframework\zend-db\src\Sql\Sql.php on line 128

तो यह हल करने के लिए आप आप query विधि के लिए कैसे किया भी इसी तरह इस विधि नकली चाहिए:

$mockStatement = //...your mocked statement... 
$this->mockDbAdapter->expects($this->once()) 
        ->method('createStatement') 
        ->will($this->returnValue($mockStatement)); 

अगली पंक्ति में आप $statement->execute(); पर कॉल करते हैं जिसका अर्थ है कि आपको अपने $mockStatement के अंदर execute विधि का नकल करने की आवश्यकता होगी।

जैसा कि आप लेखन देखते हैं यह परीक्षण बहुत बोझिल हो जाता है। और आपको खुद से पूछना चाहिए कि क्या आप सही रास्ते पर हैं और सही घटकों का परीक्षण कर रहे हैं। आप कुछ छोटे डिज़ाइन परिवर्तन (सुधार) कर सकते हैं जो आपके ProductMapper कक्षा का परीक्षण करना आसान बनाता है।

+0

ग्रेट उत्तर धन्यवाद, मुझे एक 'एसक्यूएल' उदाहरण का उपयोग करने के लिए निर्भरता को बदलने की सही दिशा में इंगित किया। अब मैंने त्रुटि संदेश से छुटकारा पा लिया है, लेकिन परीक्षण खुद विफल रहा है जहां मैं इसे पारित करने की अपेक्षा करता हूं। यदि आपने कोई नज़र डालने पर ध्यान नहीं दिया है तो मैंने प्रश्न में नया कोड जोड़ा है? मुझे यकीन नहीं है कि मैंने कुछ गड़बड़ कर ली है, या यह सिर्फ यूनिट परीक्षण में अनुभव की कमी है और मैं गलत तरीके से परीक्षा उत्तीर्ण कर रहा हूं! – crazyloonybin

+0

@ क्राज़िलोनीबिन एक कथन का नकल करने के लिए 'स्टेटमेंट इंटरफेस' का उपयोग करने के लिए पर्याप्त होगा (इसके लिए एक विशिष्ट एडाप्टर क्लास की आवश्यकता नहीं है)। फिर आपको इस '$ mockStatement' में 'execute' विधि का नकल करने की आवश्यकता है और इस विधि का वापसी मान' $ resultsSet' होना चाहिए। तब आपका परीक्षण पास होना चाहिए। – Wilt

+0

मैंने '$ this-> mockStatement = $ this-> getMock ('ज़ेंड \ डीबी \ एडाप्टर \ ड्राइवर \ स्टेटमेंट इंटरफेस') पढ़ने के लिए कथन बदल दिया है, लेकिन मुझे अभी भी एक ही त्रुटि मिल रही है। मैंने '$ this-> mockStatement' कोड को प्रश्न संपादन में दिखाए गए जैसा ही रखा है, 'testFetchAllProducts' फ़ंक्शन' $ resultsSet' वापस लौटा रहा है - क्या इस लाइन को भी संशोधित करने की आवश्यकता है? – crazyloonybin

संबंधित मुद्दे