2013-09-01 12 views
25

Engelli yapıcı ve elle ayarlanmış korumalı özellikleri olan sahte bir nesne oluşturmak mümkün mü?phpunit - mockbuilder - set nesne nesnesini iç özellik

İşte bir salakça örnektir:

class A { 
    protected $p; 
    public function __construct(){ 
     $this->p = 1; 
    } 

    public function blah(){ 
     if ($this->p == 2) 
      throw Exception(); 
    } 
} 

class ATest extend bla_TestCase { 
    /** 
     @expectedException Exception 
    */ 
    public function testBlahShouldThrowExceptionBy2PValue(){ 
     $mockA = $this->getMockBuilder('A') 
      ->disableOriginalConstructor() 
      ->getMock(); 
     $mockA->p=2; //this won't work because p is protected, how to inject the p value? 
     $mockA->blah(); 
    } 
} 

yüzden korunmaktadır p değeri enjekte etmek istiyorum, bu yüzden yapamam. Setter veya IoC'yi tanımlamalı mıyım yoksa bunu phpunit ile yapabilir miyim?

+1

Sadece kayıt için - halka açık olmayan API'yi test ediyorsanız, yanlış yapıyorsunuz demektir. Birim testi, iç uygulama değil, davranış testiyle ilgilidir. –

cevap

35

Sen Reflection kullanarak mülkiyet genel yapmak ve ardından istenen değere ayarlayabilirsiniz: Neyse

$a = new A; 
$reflection = new ReflectionClass($a); 
$reflection_property = $reflection->getProperty('p'); 
$reflection_property->setAccessible(true); 

$reflection_property->setValue($a, 2); 

sizin örnekte sen İstisna yükseltilmelidir için p değeri gerekmez. Içerik dikkate almadan, nesne davranışı üzerinde kontrol altına almak için bir alay kullanıyorsunuz.

Yani, yerine öylesine bir durum yükseltilir p = 2 belirlemek yerine vesaire yöntemi denir bir özel durum yükseltmek için mock yapılandırın:

$mockA = $this->getMockBuilder('A') 
     ->disableOriginalConstructor() 
     ->getMock(); 
$mockA->expects($this->any()) 
     ->method('blah') 
     ->will($this->throwException(new Exception)); 

Son, bu alay ediyorsun o garip ATest'te bir sınıf. Genellikle test ettiğiniz nesnenin ihtiyaç duyduğu bağımlılıkları alarsınız.

Bu yardımcı olur umarım. Her kod temeli DI ve IoC kullanılan ve böyle şeyler yapmadım eğer

/** 
* Sets a protected property on a given object via reflection 
* 
* @param $object - instance in which protected value is being modified 
* @param $property - property on instance being modified 
* @param $value - new value of the property being modified 
* 
* @return void 
*/ 
public function setProtectedProperty($object, $property, $value) 
{ 
    $reflection = new ReflectionClass($object); 
    $reflection_property = $reflection->getProperty($property); 
    $reflection_property->setAccessible(true); 
    $reflection_property->setValue($object, $value); 
} 
+1

A Sınıfı tam bağımlılık enjekte edilmedi, kurucusunda birkaç sınıfın yeni örneğini oluşturuyorum ... Bu örnekleri çözmek için kurucuyu geçersiz kılmam gerekiyor. En iyi yaklaşım değil, bunun yerine bağımlılık enjeksiyon kabını kullanacağımı düşünüyorum. – inf3rno

+0

kodunuz kesinlikle daha iyi test edilebilir olacaktır. DI'yi uygulamak için birkaç seçenek var, ama bu gerçekten basit: http://pimple.sensiolabs.org/ – gontrollez

+2

Testte bağımlılık enjeksiyon kabını kullanmayın! İyi bir birim testi sadece bir sınıfı test eder ve TÜM bağımlılıklar tamamen yapılandırılmış alaylar olarak enjekte edilir. Bunu yapamazsanız, geliştirilmeniz gereken kötü bir mimariniz var. – Sven

10

Düşünce i hızla kopyalayıp buraya yapıştırılan olabilir kullanışlı bir yardımcı yöntemini terk ediyorum

public function __construct(BlahClass $blah) 
{ 
    $this->protectedProperty = new FooClass($blah); 
} 

Yapımcıda bir sahte BlahClass kullanabilirsiniz, elbette, ancak yapıcı, atama yapamayacağınız bir şeye korumalı bir özellik ayarlar.

Muhtemelen "BlahClass yerine bir FooClass almak için kurucuyu iyi refactor yap, sonra kurucunun içinde FooClass'ı başlatmak zorunda değilsin ve bunun yerine bir sahtekarlık yapabilirsin!" Eh, haklısınız, eğer bu, tüm kod tabanı içindeki sınıfın her kullanımını, bir BlahClass yerine bir FooClass vermek için değiştirmeniz gerektiği anlamına gelmezdi.

Her kod tabanı mükemmel değildir ve bazen yalnızca işlerin yapılması gerekir. Ve bu demek oluyor ki, evet, bazen sadece "test public APIs" kuralını kırmanız gerekiyor.

-1

Şaşırtıcı olurdu:

+1

-> disableOriginalConstructor? –

İlgili konular