बहुत बार, जब यूनिट परीक्षण लिखते हैं, तो हमें इस तथ्य से निपटना होगा कि परीक्षण किया गया वर्ग बाहरी स्रोतों के डेटा पर निर्भर करता है, जिस स्थिति को हम नियंत्रित नहीं कर सकते हैं। इस तरह के स्रोतों में एक दूरगामी सार्वजनिक डेटाबेस या सेवा, कुछ शारीरिक प्रक्रिया का सेंसर आदि शामिल हैं या हमें यह सुनिश्चित करने की आवश्यकता है कि कड़ाई से परिभाषित क्रम में कुछ कार्य किए जाते हैं। इन मामलों में, मॉक ऑब्जेक्ट हमारी सहायता के लिए आते हैं (नकली अंग्रेजी में पैरोडी है), आपको बाहरी निर्भरता से अलगाव में कक्षाओं का परीक्षण करने की अनुमति देता है। PHPUnit में नकली वस्तुओं का उपयोग इस लेख के लिए समर्पित है।
उपयोग किए गए उदाहरण के रूप में, निम्न वर्ग विवरण लें:
class MyClass { protected function showWord($word) { } protected function getTemperature() { } public getWord($temparature) { $temperature = (int)$temparature; if ($temperature < 15) { return 'cold'; } if ($temperature > 25) { return 'hot'; } return 'warm'; } public function process() { $temperature = $this->getTemperature(); $word = $this->getWord($temperature); $this->showWord($word); } }
इस वर्ग की वस्तुओं को तीन तापमान स्थितियों में से एक डिवाइस पर प्रदर्शित करने के लिए डिज़ाइन किया गया है, जो परिवेश के तापमान पर निर्भर करता है। कोड लिखने के समय, न तो परिणाम प्रदर्शित करने के लिए उपकरण, न ही तापमान सेंसर उपलब्ध हैं, और उन्हें एक्सेस करने का प्रयास कार्यक्रम में विफलता का कारण बन सकता है।
सबसे सरल मामले में, तर्क को सत्यापित करने के लिए, हम निर्दिष्ट वर्ग से वारिस कर सकते हैं, स्टब विधियों की जगह ले सकते हैं जो असंबद्ध उपकरणों तक पहुंचते हैं, और वंशज उदाहरण पर इकाई परीक्षण करते हैं। PHPUnit में नकली वस्तुओं को लगभग उसी तरह से लागू किया जाता है, जहां अंतर्निहित API के रूप में अतिरिक्त सुविधा प्रदान की जाती है।
मॉक ऑब्जेक्ट प्राप्त करना
मॉक ऑब्जेक्ट का एक उदाहरण प्राप्त करने के लिए, getMock () विधि का उपयोग करें:
class MyClassTest extends PHPUnit_Framework_TestCase { public function test_process() { $mock = $this->getMock('MyClass');
जैसा कि आप देख सकते हैं, हमें जिस मॉक ऑब्जेक्ट की आवश्यकता है वह बहुत सरल है। डिफ़ॉल्ट रूप से, इसमें सभी विधियों को उन स्टब्स द्वारा प्रतिस्थापित किया जाएगा जो कुछ भी नहीं करते हैं और हमेशा अशक्त लौटते हैं।
GetMock कॉल विकल्प
public function getMock( $originalClassName, // , Mock $methods = array(), // array $arguments = array(), // , $mockClassName = '', // Mock $callOriginalConstructor = true, // __construct() $callOriginalClone = true, // __clone() $callAutoload = true // __autoload() );
पास करने के लिए दूसरे तर्क के रूप में बिल्डर को पास करने के लिए getMock () पास करने से मॉक ऑब्जेक्ट को बिना किसी प्रतिस्थापन के वापस कर दिया जाएगा।
getMockBuilder
जो लोग श्रृंखला शैली में लिखना पसंद करते हैं, उनके लिए PHPUnit एक उपयुक्त निर्माता प्रदान करता है:
$mock = $this->getMockBuilder('MyClass') ->setMethods(null) ->setConstructorArgs(array()) ->setMockClassName('')
श्रृंखला हमेशा getMockBuilder () विधि से शुरू होनी चाहिए और getMock () विधि के साथ डाउनलोड की जानी चाहिए - ये श्रृंखला में एकमात्र लिंक हैं जो आवश्यक हैं।
नकली वस्तुओं को प्राप्त करने के अतिरिक्त तरीके
- getMockFromWsdl () - आपको WSDL के विवरण के आधार पर मॉक ऑब्जेक्ट बनाने की अनुमति देता है;
- getMockClass () - एक मॉक क्लास बनाता है और एक स्ट्रिंग के रूप में अपना नाम देता है;
- getMockForAbstractClass () - एक अमूर्त वर्ग का एक नकली ऑब्जेक्ट देता है जिसमें सभी अमूर्त विधियों को प्रतिस्थापित किया जाता है।
यह सब अद्भुत है - आप कहते हैं, लेकिन आगे क्या? जवाब में, मैं कहूंगा कि हम अभी सबसे दिलचस्प आए।
एक विधि कॉल की प्रतीक्षा की जा रही है
PHPUnit हमें प्रतिस्थापित विधियों के लिए कॉल की संख्या और क्रम को नियंत्रित करने की अनुमति देता है। ऐसा करने के लिए, विधि () का उपयोग करके वांछित विधि द्वारा पीछा किए गए एक्सपेक्ट्स () निर्माण का उपयोग करें। एक उदाहरण के रूप में, आइए लेख की शुरुआत में कक्षा को देखें और इसके लिए निम्नलिखित परीक्षा लिखें:
public function test_process() { $mock = $this->getMock('MyClass', array('getTemperature', 'getWord', 'showWord')); $mock->expects($this->once())->method('getTemperature'); $mock->expects($this->once())->method('showWord'); $mock->expects($this->once())->method('getWord'); $mock->process(); }
इस परीक्षण का परिणाम तब सफल होगा, जब प्रक्रिया () विधि को बुलाते समय, तीन सूचीबद्ध विधियों के लिए एक एकल कॉल होता है: गेटटेंस (), गेटवॉर्ड (), शोवर्ल्ड ()। ध्यान दें कि परीक्षण में, getWord () को कॉल को showWord () को कॉल करने के बाद जांचा जाता है, हालांकि परीक्षण विधि में विपरीत सत्य है। यह सही है, यहां कोई त्रुटि नहीं है। PHPUnit में विधि कॉल के आदेश को नियंत्रित करने के लिए, एक और निर्माण का उपयोग किया जाता है - पर ()। आइए हमारे परीक्षण कोड को थोड़ा ठीक करें ताकि PHPUnit उसी समय विधि कॉल के अनुक्रम की जाँच करें:
public function test_process() { $mock = $this->getMock('MyClass', array('getTemperature', 'getWord', 'showWord')); $mock->expects($this->at(0))->method('getTemperature'); $mock->expects($this->at(2))->method('showWord'); $mock->expects($this->at(1))->method('getWord'); $mock->process(); }
PHPUnit में कॉल अपेक्षाओं के परीक्षण के लिए एक बार () और पर () उल्लेख के अलावा, निम्नलिखित निर्माण भी हैं: कोई भी (), कभी नहीं (), कम से कम () और बिल्कुल ($ गिनती)। उनके नाम खुद बोलते हैं।
परिणाम ओवरराइड करें
अब तक, मॉक ऑब्जेक्ट्स की सबसे उपयोगी विशेषता स्पूफेड विधियों द्वारा लौटाए गए परिणाम का अनुकरण करने की क्षमता है। हम फिर से अपनी कक्षा की प्रक्रिया () पद्धति की ओर मुड़ते हैं। हम वहां तापमान सेंसर के लिए एक अपील देखते हैं - गेटटैन्स ()। लेकिन हमें यह भी याद है कि वास्तव में हमारे पास सेंसर नहीं है। हालांकि अगर हमारे पास यह था, तो भी हम सभी संभावित स्थितियों का परीक्षण करने के लिए इसे 15 डिग्री से नीचे या 25 से ऊपर गर्म नहीं करेंगे। जैसा कि आप अनुमान लगा सकते हैं, इस मामले में मॉक ऑब्जेक्ट हमारी सहायता के लिए आते हैं। हम अपनी इच्छानुसार कोई भी परिणाम लौटा सकते हैं। यहाँ एक उदाहरण है:
public function test_process($temperature) { $mock = $this->getMock('MyClass', array('getTemperature', 'getWord', 'showWord'));
जाहिर है, इस परीक्षा में उन सभी संभावित मूल्यों को शामिल किया गया है, जिन्हें हमारा परीक्षण वर्ग संभाल सकता है। PHPUnit वसीयत के साथ उपयोग के लिए निम्नलिखित निर्माण प्रदान करता है ():
- returnValue ($ मूल्य) - $ मूल्य लौटाता है;
- returnArgument ($ index) - पद्धति को कॉल किए जाने पर निर्दिष्ट $ $ सूचकांक के साथ तर्क लौटाता है;
- returnSelf () - अपने आप में एक पॉइंटर लौटाता है, जो श्रृंखला विधियों के परीक्षण के लिए उपयोगी है;
- returnValueMap ($ नक्शा) - तर्क के विशिष्ट सेटों के आधार पर परिणाम वापस करने के लिए उपयोग किया जाता है;
- वापसी कॉलबैक ($ कॉलबैक) - निर्दिष्ट फ़ंक्शन के लिए तर्क पारित करता है और इसका परिणाम देता है;
- onConsistentCalls () - क्रमिक रूप से प्रत्येक बाद की विधि कॉल के साथ सूचीबद्ध तर्कों में से एक देता है;
- फेंक अपवाद ($ अपवाद) - निर्दिष्ट अपवाद फेंकता है।
मान्य तर्क
परीक्षण के लिए मॉक ऑब्जेक्ट्स की एक अन्य उपयोगी विशेषता निर्माण () निर्माण के साथ एक स्पूफ़ विधि का उपयोग करते समय निर्दिष्ट तर्कों की जाँच कर रही है:
public function test_with_and_will_usage() { $mock = $this->getMock('MyClass', array('getWord')); $mock->expects($this->once()) ->method('getWord') ->with($this->greaterThan(25)) ->will($this->returnValue('hot')); $this->assertEquals('hot', $mock->getWord(30)); }
जैसा कि तर्क () सभी निर्माणों को मुखर () जांच के रूप में ले सकते हैं, इसलिए यहां मैं एक विस्तृत विवरण के बिना केवल संभावित निर्माणों की एक सूची प्रदान करूंगा:
- विशेषता ()
- कुछ भी ()
- arrayHasKey ()
- शामिल हैं ()
- बराबर ()
- विशेषताएक्लो ()
- fileExists ()
- अधिकता ()
- अधिक
- classHasAttribute ()
- classHasStaticAttribute ()
- hasAttribute ()
- समरूप ()
- फ़ाल्स () है
- IsstanceOf ()
- है। नल ()
- IsTrue ()
- isType ()
- लोथन ()
- कम
- माचिस
- stringContains ()
इन सभी निर्माणों को तार्किक कंस्ट्रक्ट्स लॉजिकलएंड (), लॉजिकलऑर (), लॉजिकलनॉट () और लॉजिकलऑनर () का उपयोग करके जोड़ा जा सकता है:
$mock->expects($this->once()) ->method('getWord') ->with($this->logicalAnd($this->greaterThanOrEqual(15), $this->lessThanOrEqual(25))) ->will($this->returnValue('warm'));
अब जब हम PHPUnit में नकली वस्तुओं की क्षमताओं से पूरी तरह परिचित हैं, तो हम अपनी कक्षा का अंतिम परीक्षण कर सकते हैं:
public function test_process($temperature, $expected_word) {
UPD:
VolCh ने ठीक ही
टिप्पणी की है कि ऊपर प्रस्तुत किया गया जैसे लेखन परीक्षण
एंटीपैटर्न है। इसलिए, उपरोक्त उदाहरण को केवल PHPUnit में मॉक ऑब्जेक्ट्स की क्षमताओं के साथ खुद को परिचित करने के लिए माना जाना चाहिए।
स्थैतिक विधि प्रतिस्थापन
PHPUnit संस्करण 3.5 के साथ शुरू करना, स्थैतिक निर्माण staticExpects () का उपयोग करके स्थिर तरीकों को बदलना संभव है:
$class = $this->getMockClass('SomeClass');
इस नवाचार के लिए व्यावहारिक अनुप्रयोग होने के लिए, यह आवश्यक है कि परीक्षण किए गए वर्ग के अंदर बदली हुई स्थिर विधि का कॉल निम्न में से किसी एक में हो:
- $ यह-> staticMethod () - गतिशील तरीकों से;
- स्थिर :: staticMethod () - स्थैतिक और गतिशील तरीकों से।
स्वयं की
सीमाओं के कारण
, क्लास के अंदर इस तरह से कहे जाने वाले स्थिर तरीकों का प्रतिस्थापन काम नहीं करेगा:
self::staticMethod();
निष्कर्ष
अंत में, मैं कहूंगा कि आपको बाहरी डेटा स्रोतों से परीक्षण किए गए वर्ग को अलग करने के अलावा किसी भी उद्देश्य के लिए मॉक ऑब्जेक्ट्स के साथ बहुत दूर नहीं जाना चाहिए। अन्यथा, किसी भी, यहां तक कि मामूली के साथ, स्रोत कोड में परिवर्तन, आपको सबसे अधिक संभावना है कि खुद को भी परीक्षण संपादित करने की आवश्यकता होगी। एक काफी महत्वपूर्ण रीफ़ैक्टरिंग इस तथ्य को जन्म दे सकता है कि आपको उन्हें पूरी तरह से फिर से लिखना होगा।