नकली फ़र्ज़ी मॉक के लिए कंस्ट्रक्टर के तर्क से बचें


85

फ़ोकुनिट को मॉक ऑब्जेक्ट के लिए कंस्ट्रक्टर को कॉल करने से बचने का तरीका क्या है? अन्यथा मुझे कंस्ट्रक्टर तर्क के रूप में एक नकली वस्तु की आवश्यकता होगी, उसके लिए एक और एक आदि। एपीआई इस तरह प्रतीत होता है:

getMock($className, $methods = array(), array $arguments = array(),
        $mockClassName = '', $callOriginalConstructor = TRUE,
        $callOriginalClone = TRUE, $callAutoload = TRUE)

मुझे यह काम करने के लिए नहीं मिलता है। यह अभी भी निर्माता के तर्क के बारे में शिकायत करता है, यहां तक ​​कि $callOriginalConstructorझूठे के लिए भी सेट करता है।

मेरे पास कंस्ट्रक्टर में केवल एक ऑब्जेक्ट है और यह एक निर्भरता इंजेक्शन है। इसलिए मुझे नहीं लगता कि मुझे वहाँ कोई डिज़ाइन समस्या है।

जवाबों:


139

तुम getMockBuilderसिर्फ के बजाय उपयोग कर सकते हैं getMock:

$mock = $this->getMockBuilder('class_name')
    ->disableOriginalConstructor()
    ->getMock();

विवरण के लिए PHPUnit के दस्तावेज में "टेस्ट डबल्स" पर अनुभाग देखें।

हालाँकि आप ऐसा कर सकते हैं, लेकिन इसकी ज़रूरत नहीं है। आप एक ठोस वर्ग (एक निर्माता के साथ) को इंजेक्शन लगाने की आवश्यकता के बजाय अपने कोड को रीफ़ैक्टर कर सकते हैं, आप केवल एक इंटरफ़ेस पर निर्भर करते हैं। इसका मतलब है कि आप PHPUnit को बताए बिना इंटरफ़ेस को मॉक या स्टब कर सकते हैं।


यह मेरे लिए बहुत अच्छा काम करता है। यह उदाहरण 10.3 होना चाहिए, हालांकि। मैंने पोस्ट को संपादित करने की कोशिश की, लेकिन एसओ ने इसे संपादित करने के लिए बहुत छोटा कर दिया।
मैथ्यू

धन्यवाद @Lytithwyn उत्तर अपडेट किया गया।
dave1010

42

हेयर यू गो:

    // Get a Mock Soap Client object to work with.
    $classToMock = 'SoapClient';
    $methodsToMock = array('__getFunctions');
    $mockConstructorParams = array('fake wsdl url', array());
    $mockClassName = 'MyMockSoapClient';
    $callMockConstructor = false;
    $mockSoapClient = $this->getMock($classToMock,
                                     $methodsToMock,
                                     $mockConstructorParams,
                                     $mockClassName,
                                     $callMockConstructor);

यह लगभग मैं क्या चाहता हूँ लगता है। मैं getMock को केवल वर्ग के साथ नकली और $ callMockConstructor कहना चाहता हूं। कैसे? कुछ इस तरह से: $ इस-> getMock ($ classToMock, $ callMockConstructor)। केवल एक चीज जिसके बारे में मैं सोच सकता था वह है PHPUnit के स्रोत में जाना और इसे डिफ़ॉल्ट = गलत में बदलना।
गुत्ज़ोफ़्टर

1
मैंने डिफ़ॉल्ट को टेस्टकेस में गलत बदल दिया है। एफपी। आपको लगता है कि यह डिफ़ॉल्ट रूप से गलत के लिए सेट किया जाएगा। एक निर्माता का
मज़ाक उड़ाना

बहुत बढ़िया जवाब। बस मैं क्या देख रहा था
हेड्स

4

एक परिशिष्ट के रूप में, मैं expects()अपनी नकली वस्तु पर कॉल संलग्न करना चाहता था और फिर कंस्ट्रक्टर को कॉल करूंगा। PHPUnit 3.7.14 में, जब आप कॉल करते हैं, तो वह वस्तु disableOriginalConstructor()वस्तुतः एक वस्तु होती है।

// Use a trick to create a new object of a class
// without invoking its constructor.
$object = unserialize(
sprintf('O:%d:"%s":0:{}', strlen($className), $className)

दुर्भाग्य से, PHP 5.4 में एक नया विकल्प है जिसका वे उपयोग नहीं कर रहे हैं:

ReflectionClass :: newInstanceWithoutConstructor

चूंकि यह उपलब्ध नहीं था, इसलिए मुझे मैन्युअल रूप से क्लास को प्रतिबिंबित करना पड़ा और फिर कंस्ट्रक्टर को आमंत्रित करना पड़ा।

$mock = $this->getMockBuilder('class_name')
    ->disableOriginalConstructor()
    ->getMock();

$mock->expect($this->once())
    ->method('functionCallFromConstructor')
    ->with($this->equalTo('someValue'));

$reflectedClass = new ReflectionClass('class_name');
$constructor = $reflectedClass->getConstructor();
$constructor->invoke($mock);

ध्यान दें, यदि functionCallFromConstructहै protected, तो आपको विशेष रूप से उपयोग करना होगा setMethods()ताकि संरक्षित विधि का मजाक उड़ाया जाए। उदाहरण:

    $mock->setMethods(array('functionCallFromConstructor'));

setMethods()expect()कॉल से पहले बुलाया जाना चाहिए । व्यक्तिगत रूप से, मैं इसके बाद disableOriginalConstructor()लेकिन पहले श्रृंखला करता हूं getMock()


कोई विचार नहीं अगर यह एक कोड गंध है, लेकिन यह मेरे लिए बहुत अच्छा काम करता है और मैं सिर्फ आपको धन्यवाद देना चाहता था।
देवबन

1

शायद आपको कंस्ट्रक्टर तर्क के रूप में पारित करने के लिए एक स्टब बनाने की आवश्यकता है। तब आप नकली वस्तुओं की उस श्रृंखला को तोड़ सकते हैं।


1

वैकल्पिक रूप से आप डिफ़ॉल्ट निर्माणकर्ता की कॉल को रोकने के लिए getMock में एक पैरामीटर जोड़ सकते हैं ।

$mock = $this->getMock(class_name, methods = array(), args = array(), 
        mockClassName = '', callOriginalConstructor = FALSE);

फिर भी, मुझे लगता है कि dave1010 का उत्तर अच्छा लग रहा है, यह पूर्णता के लिए है।


1

यह सवाल थोड़ा पुराना है, लेकिन नए आगंतुकों के लिए, आप इसे createMockविधि का उपयोग करके कर सकते हैं (पहले कहा गया createTestDoubleऔर v5.4.0 में पेश किया गया)।

$mock = $this->createMock($className);

जैसा कि आप PHPUnit\Framework\TestCaseकक्षा ( phpunit/src/framework/TestCase.php) में से निकाले गए नीचे दिए गए कोड में देख सकते हैं , यह मूल रूप से मूल निर्माता को बुलाए बिना एक नकली वस्तु बनाएगा

/** PHPUnit\Framework\TestCase::createMock method */
protected function createMock(string $originalClassName): MockObject
{
    return $this->getMockBuilder($originalClassName)
                ->disableOriginalConstructor()
                ->disableOriginalClone()
                ->disableArgumentCloning()
                ->disallowMockingUnknownTypes()
                ->getMock();
}

0

PHPUnit को नकली वस्तुओं पर कंस्ट्रक्टर को कॉल करने के लिए डिज़ाइन किया गया है; इसे रोकने के लिए आपको या तो:

  1. एक मॉक ऑब्जेक्ट को उस ऑब्जेक्ट पर निर्भरता के रूप में इंजेक्ट करें जिसमें आपको मॉकिंग करने में परेशानी हो रही है
  2. एक परीक्षण वर्ग बनाएं जो उस कक्षा को विस्तारित करता है जिसे आप कॉल करने का प्रयास कर रहे हैं जो मूल निर्माता को कॉल नहीं करता है
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.