PHPUnit ile korumalı yöntemleri test etmek için en iyi uygulamaları [kapalı]

8 Cevap php

I Do you test private method bilgilendirici üzerinde tartışma bulundu.

I have deckimliked, that in some classes, I want to have protected methods, but test them. Some of these methods are static and short. Because most of the public methods make use of them, I will probably be able to safely remove the tests later. But for starting with a TDD approach and avokimlik debugging, I really want to test them.

Ben şu düşündüm:

  • Method Object tavsiye olarak an answer, bu için overkill gibi görünüyor.
  • Kamu yöntemleri ile başlayın ve kod kapsama üst düzey testleri tarafından verildiğinde, onları korumalı açmak ve testleri çıkarın.
  • Test edilebilir bir arayüz korumalı yöntemleri kamu yapma ile bir sınıf miras

En iyi uygulama hangisi? Başka bir şey var mı?

Bu JUnit otomatik olarak ortak olmak korumalı yöntemleri değişir ki, görünüyor, ama ben ona daha derin bir göz yoktu. PHP reflection ile bu izin vermez.

8 Cevap

Eğer PHPUnit ile PHP5 (> = 5.3.2) kullanılarak ediyorsanız, öncesinde testleri çalışan kamu olmak için onları ayarlamak için yansıma kullanarak özel ve korumalı yöntemleri test edebilirsiniz:

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(...));
  ...
}

Sen zaten farkında gibi görünüyor, ama ben sadece yine de düzeltilmesinde edeceğiz; Eğer korumalı yöntemleri test etmek gerekir eğer, kötü bir işaret. Bir birim test amacı, bir sınıfın arayüzünü test etmek, ve korumalı yöntemleri uygulama detayları vardır. O dedi, bu mantıklı durumlar vardır. Eğer devralma kullanırsanız, alt sınıf için bir arayüz sağlamak gibi bir üst sınıfı görebilirsiniz. Yani burada, sen korumalı yöntemi test etmek gerekir (Ama asla bir private bir). Bunun çözümü, test amaçlı bir alt sınıf oluşturmak ve yöntemleri göstermek için bu kullanmaktır. Örn.:

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

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

Her zaman kompozisyon kalıtım değiştirebilirsiniz unutmayın. Kodu test ederken, genellikle bu modeli kullanan kod ile başa çıkmak için çok daha kolay, bu nedenle bu seçeneği düşünebilirsiniz.

teastburn doğru bir yaklaşım vardır. Hatta basit doğrudan yöntemini çağırın ve yanıt dönmek için:

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

Bu testlerde sadece çağrılabilir:

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

I uckelman's answer tanımlanan GetMethod hafif bir varyasyonu () önermek istiyorum.

Bu sürüm sabit kodlanmış değerlere çıkarılması ve kullanımı biraz basitleştirerek GetMethod () değiştirir. Ben (global PHPUnitUtil dosyasına, sanırım, ya da) aşağıdaki örnekte veya PHPUnit_Framework_TestCase-uzanan sınıf olarak PHPUnitUtil sınıfına ekleyerek öneririz.

Sınıfım neyse örneği olan ve ReflectionClass bir dize ya da bir nesneyi alabilir yana ...

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 $method 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
}

Ben de bir takma işlevi getProtectedMethod () ne beklendiği açık olmak, ama o kişinin size kalmış yarattı.

Şerefe!

Ben troelskn yakın olduğunu düşünüyorum. Ben bunun yerine yapardı:

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

Sonra, bu gibi bir şey uygulamak:

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

Daha sonra TestClassToTest karşı testleri çalıştırın.

Otomatik kod ayrıştırma böyle uzatma sınıflar oluşturmak mümkün olmalıdır. PHPUnit zaten (ben kontrol etmedim ama) böyle bir mekanizma sunuyor eğer sürpriz olmaz.

Ben "Henrik Paul" 'un çözüm / fikir için çözüm uygulamanızı öneririz :)

Siz kendi sınıfının özel yöntemleri isimlerini biliyorum. Örneğin onlar) (_add gibi, _edit (), _delete (vb)

Eğer birim test açıdan test etmek istediğinizde Bu nedenle, sadece (örneğin _addPhpunit) bazı common kelimeyi önek ve / veya suffixing tarafından özel yöntemler diyoruz ki __ call () yöntemi çağrıldığında (beri yöntem _addPhpunit () sahibi sınıfın) yok, sadece önceden sabitlenmiş / ekli kelime / s (phpunit) kaldırmak ve daha sonra oradan o türetilmiş özel bir yöntemi çağırmak için __ () yöntemine çağrı gerekli kod koymak. Bu sihirli yöntemlerin bir iyi kullanmaktır.

Bunu bir deneyin.

Siz gerçekten korumalı yöntemlerine erişmek için genel bir şekilde __ çağrısı () kullanabilirsiniz. Bu sınıf test edebilmek için

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

Eğer ExampleTest.php bir alt sınıf oluşturun:

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

Eğer test etmek ve sadece sınıf bildirimi değiştirmek istediğiniz korumalı yöntemleri ile her sınıf için yukarıdaki kopyalayabilirsiniz böylece __ call () yöntemi herhangi bir şekilde sınıfı başvuracak unutmayın. Sen ortak bir taban sınıfta bu işlevi yerleştirmek mümkün olabilir, ama ben denemedim.

Şimdi test durumda kendisi sadece Örnek ExampleExposed de takas, size test edilecek nesne oluşturmak nerede farklıdır.

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

PHP 5.3 doğrudan yöntemlerin erişilebilirliğini değiştirmek için yansıma kullanmak için izin verir inanıyorum, ama ben tek tek her yöntemi için bunu yapmak gerekiyor varsayalım.

Burada halka içine benim şapka atmak için gidiyorum:

I've used the __call hack with mixed degrees of success. The alternative I came up with was to use the Visitor pattern:

1: Bir stdclass veya özel sınıf oluşturmak (türünü zorlamak için)

2: asal ki gerekli yöntem ve argümanları ile

3: SUT ziyaret sınıfta belirtilen argümanlar ile yöntemini yürütecek bir acceptVisitor yöntem olduğundan emin

4: Eğer test etmek isteyen sınıf içine enjekte

5: SUT ziyaretçinin içine operasyonun sonucunu enjekte

6: Ziyaretçi sonuç özniteliği için test koşulları geçerlidir