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




unit-testing (7)

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

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

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

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

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

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


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

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

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

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

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

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


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)
             );

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

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

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

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


मैं 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: विज़िटर के परिणाम विशेषता में अपनी परीक्षा शर्तों को लागू करें


यदि आप PHPUnit के साथ PHP5 (> = 5.3.2) का उपयोग कर रहे हैं, तो आप अपने परीक्षणों को चलाने से पहले सार्वजनिक होने के लिए प्रतिबिंब का उपयोग कर अपने निजी और संरक्षित तरीकों का परीक्षण कर सकते हैं:

protected static function getMethod($name) {
  $class = new ReflectionClass('MyClass');
  $method = $class->getMethod($name);
  $method->setAccessible(true);
  return $method;
}

public function testFoo() {
  $foo = self::getMethod('foo');
  $obj = new MyClass();
  $foo->invokeArgs($obj, array(...));
  ...
}

मैं "हेनरिक पॉल" के कामकाज / विचार के लिए निम्नलिखित कार्यवाही का सुझाव देता हूं :)

आप अपनी कक्षा के निजी तरीकों के नाम जानते हैं। उदाहरण के लिए वे _add (), _edit (), _delete () आदि की तरह हैं।

इसलिए जब आप इसे यूनिट-परीक्षण के पहलू से परीक्षण करना चाहते हैं, तो बस कुछ सामान्य शब्द (उदाहरण के लिए _addPhunit) को उपसर्ग और / या प्रत्यय द्वारा निजी विधियों को कॉल करें ताकि जब __call () विधि को कॉल किया जाए (क्योंकि विधि _addPhpunit () नहीं है मौजूद है) मालिक वर्ग के, आप केवल prefixed / suffixed शब्द / ph (phpunit) को हटाने के लिए __call () विधि में आवश्यक कोड डालते हैं और उसके बाद उस से निकाली गई निजी विधि को कॉल करने के लिए। यह जादू तरीकों का एक और अच्छा उपयोग है।

कोशिश करके देखो।





phpunit