PHPUnit के साथ संरक्षित विधियों का परीक्षण करने के लिए सर्वोत्तम अभ्यास




unit-testing (6)

आपको पहले से ही पता होना प्रतीत होता है, लेकिन मैं इसे वैसे भी पुन: स्थापित करूंगा; यदि आपको संरक्षित तरीकों का परीक्षण करने की आवश्यकता है, तो यह एक बुरा संकेत है। एक इकाई परीक्षण का उद्देश्य, कक्षा के इंटरफेस का परीक्षण करना है, और संरक्षित तरीके कार्यान्वयन विवरण हैं। उस ने कहा, ऐसे मामले हैं जहां यह समझ में आता है। यदि आप विरासत का उपयोग करते हैं, तो आप सबक्लास के लिए इंटरफ़ेस प्रदान करने के रूप में एक सुपरक्लास देख सकते हैं। तो यहां, आपको संरक्षित विधि का परीक्षण करना होगा (लेकिन कभी भी एक निजी नहीं)। इसका समाधान, परीक्षण उद्देश्य के लिए उप-वर्ग बनाना है, और विधियों का पर्दाफाश करने के लिए इसका उपयोग करना है। उदाहरण के लिए .:

class Foo {
  protected function stuff() {
    // secret stuff, you want to test
  }
}

class SubFoo extends Foo {
  public function exposedStuff() {
    return $this->stuff();
  }
}

ध्यान दें कि आप हमेशा विरासत को संरचना के साथ बदल सकते हैं। कोड का परीक्षण करते समय, आमतौर पर इस पैटर्न का उपयोग करने वाले कोड से निपटना बहुत आसान होता है, इसलिए आप उस विकल्प पर विचार करना चाहेंगे।

मैंने चर्चा की है कि क्या आप निजी विधि सूचनात्मक परीक्षण करते हैं।

मैंने फैसला किया है कि कुछ वर्गों में, मैं संरक्षित तरीकों को रखना चाहता हूं, लेकिन उनका परीक्षण करें। इनमें से कुछ विधियां स्थिर और छोटी हैं। चूंकि अधिकांश सार्वजनिक विधियां उनका उपयोग करती हैं, इसलिए मैं बाद में परीक्षणों को सुरक्षित रूप से सुरक्षित कर पाऊंगा। लेकिन एक टीडीडी दृष्टिकोण से शुरू करने और डीबगिंग से बचने के लिए, मैं वास्तव में उनका परीक्षण करना चाहता हूं।

मैंने निम्नलिखित के बारे में सोचा:

  • एक उत्तर में सलाह के रूप में विधि वस्तु इस के लिए overkill लगता है।
  • सार्वजनिक तरीकों से शुरू करें और जब उच्च स्तर के परीक्षणों द्वारा कोड कवरेज दिया जाता है, तो उन्हें सुरक्षित रखें और परीक्षण हटा दें।
  • संरक्षित तरीकों को सार्वजनिक रूप से संरक्षित करने वाले टेस्टेबल इंटरफ़ेस वाले क्लास को विरासत में रखें

सबसे अच्छा अभ्यास कौन सा है? क्या कुछ और है?

ऐसा लगता है कि जुनीट स्वचालित रूप से संरक्षित तरीकों को सार्वजनिक करने के लिए बदलता है, लेकिन मुझे इसकी गहराई से नजर नहीं आया। PHP इसे reflection माध्यम से अनुमति नहीं देता है।


मुझे लगता है कि troelskn करीब है। मैं इसके बजाय ऐसा करूंगा:

class ClassToTest
{
   protected testThisMethod()
   {
     // Implement stuff here
   }
}

फिर, इस तरह कुछ लागू करें:

class TestClassToTest extends ClassToTest
{
  public testThisMethod()
  {
    return parent::testThisMethod();
  }
}

फिर आप TestClassToTest के खिलाफ अपने परीक्षण चलाते हैं।

कोड को पार्स करके स्वचालित रूप से ऐसे एक्सटेंशन वर्गों को उत्पन्न करना संभव होना चाहिए। मुझे आश्चर्य नहीं होगा अगर PHPUnit पहले से ही इस तरह की एक तंत्र प्रदान करता है (हालांकि मैंने चेक नहीं किया है)।


मैं uckelman के उत्तर में परिभाषित getMethod () को थोड़ी भिन्नता का प्रस्ताव देना चाहता हूं।

यह संस्करण हार्ड-कोड किए गए मानों को हटाकर और उपयोग को सरल बनाकर getMethod () को बदलता है। मैं इसे नीचे दिए गए उदाहरण में या आपके PHPUnit_Framework_TestCase-Extending क्लास (या, मुझे लगता है, वैश्विक रूप से आपके PHPUnitUtil फ़ाइल में) के रूप में इसे अपने PHPUnitUtil क्लास में जोड़ने की अनुशंसा करते हैं।

चूंकि MyClass को तत्काल चालू किया जा रहा है और ReflectionClass एक स्ट्रिंग या ऑब्जेक्ट ले सकता है ...

class PHPUnitUtil {
    /**
     * Get a private or protected method for testing/documentation purposes.
     * How to use for MyClass->foo():
     *      $cls = new MyClass();
     *      $foo = PHPUnitUtil::getPrivateMethod($cls, 'foo');
     *      $foo->invoke($cls, $...);
     * @param object $obj The instantiated instance of your class
     * @param string $name The name of your private/protected method
     * @return ReflectionMethod The method you asked for
     */
    public static function getPrivateMethod($obj, $name) {
      $class = new ReflectionClass($obj);
      $method = $class->getMethod($name);
      $method->setAccessible(true);
      return $method;
    }
    // ... some other functions
}

मैंने एक उपनाम फ़ंक्शन getProtectedMethod () को स्पष्ट करने के लिए भी बनाया है, लेकिन यह आपके ऊपर है।

चीयर्स!


मैं अपनी टोपी को अंगूठी में फेंकने जा रहा हूं:

मैंने सफलता की मिश्रित डिग्री के साथ __call हैक का उपयोग किया है। जिस विकल्प के साथ मैं आया था वह विज़िटर पैटर्न का उपयोग करना था:

1: एक stdClass या कस्टम वर्ग उत्पन्न करें (प्रकार लागू करने के लिए)

2: प्राइम कि आवश्यक विधि और तर्क के साथ

3: सुनिश्चित करें कि आपके एसयूटी के पास एक स्वीकार्य विधि है जो विज़िटिंग क्लास में निर्दिष्ट तर्कों के साथ विधि को निष्पादित करेगी

4: उस कक्षा में इंजेक्ट करें जिसे आप परीक्षण करना चाहते हैं

5: एसयूटी आगंतुक में ऑपरेशन के परिणाम इंजेक्ट करता है

6: विज़िटर के परिणाम विशेषता में अपनी परीक्षा शर्तों को लागू करें


संरक्षित तरीकों तक पहुंचने के लिए आप वास्तव में __call () को सामान्य रूप से उपयोग कर सकते हैं। इस वर्ग का परीक्षण करने में सक्षम होने के लिए

class Example {
    protected function getMessage() {
        return 'hello';
    }
}

आप exampleTest.php में उप-वर्ग बनाते हैं:

class ExampleExposed extends Example {
    public function __call($method, array $args = array()) {
        if (!method_exists($this, $method))
            throw new BadMethodCallException("method '$method' does not exist");
        return call_user_func_array(array($this, $method), $args);
    }
}

ध्यान दें कि __call () विधि क्लास को किसी भी तरह से संदर्भित नहीं करती है ताकि आप उपरोक्त प्रतियों को संरक्षित विधियों के साथ प्रतिलिपि बना सकें जिन्हें आप परीक्षण करना चाहते हैं और केवल वर्ग घोषणा को बदल दें। आप इस फ़ंक्शन को एक सामान्य बेस क्लास में रखने में सक्षम हो सकते हैं, लेकिन मैंने इसे आजमाया नहीं है।

अब टेस्ट केस केवल उस स्थान पर भिन्न होता है जहां आप परीक्षण के लिए ऑब्जेक्ट का निर्माण करते हैं, उदाहरण के लिए उदाहरण एक्सपोज़ड में स्वैपिंग।

class ExampleTest extends PHPUnit_Framework_TestCase {
    function testGetMessage() {
        $fixture = new ExampleExposed();
        self::assertEquals('hello', $fixture->getMessage());
    }
}

मेरा मानना ​​है कि PHP 5.3 आपको विधियों की पहुंच को सीधे बदलने के लिए प्रतिबिंब का उपयोग करने की अनुमति देता है, लेकिन मुझे लगता है कि आपको प्रत्येक विधि के लिए व्यक्तिगत रूप से ऐसा करना होगा।


teastburn सही दृष्टिकोण है। विधि को सीधे कॉल करना और जवाब देना भी आसान है:

class PHPUnitUtil
{
  public static function callMethod($obj, $name, array $args) {
        $class = new \ReflectionClass($obj);
        $method = $class->getMethod($name);
        $method->setAccessible(true);
        return $method->invokeArgs($obj, $args);
    }
}

आप इसे अपने परीक्षणों में आसानी से कॉल कर सकते हैं:

$returnVal = PHPUnitUtil::callMethod(
                $this->object,
                '_nameOfProtectedMethod', 
                array($arg1, $arg2)
             );




phpunit