2009-07-22 10 views
20

Próbuję przenieść kilka testów z SimpleTest do PHPUnit i zastanawiałem się, czy istnieje odpowiednik dla SimpleTest's partial mocks.Równoważnik prostych "częściowych mocków" w PHPUnit?

Aktualizacja: Nie mogę znaleźć niczego w dokumentach sugerujących, że ta funkcja jest dostępna, ale przyszło mi do głowy, że mogę po prostu użyć podklasy. Czy to dobry czy zły pomysł?

class StuffDoer { 
    protected function doesLongRunningThing() { 
     sleep(10); 
     return "stuff"; 
    } 
    public function doStuff() { 
     return $this->doesLongRunningThing(); 
    } 
} 
class StuffDoerTest { 
    protected function doesLongRunningThing() { 
     return "test stuff"; 
    } 
} 
class StuffDoerTestCase extends PHPUnit_Framework_TestCase { 
    public function testStuffDoer() { 
     $sd = new StuffDoerTest(); 
     $result = $sd->doStuff(); 
     $this->assertEquals($result, "test stuff"); 
    } 
} 
+0

Dzięki za pomysł na podklasę! – Kyslik

Odpowiedz

40

z lektury połączoną stronę, o SimpleTest częściowy mock wydaje się być fałszywa, gdzie tylko niektóre z metod są zastępowane. Jeśli jest to poprawne, ta funkcjonalność jest obsługiwana przez normalną próbkę PHPUnit.

Wewnątrz PHPUnit_Framework_TestCase, tworzenia makiety z

$mock = $this->getMock('Class_To_Mock'); 

który tworzy makiety instancji gdzie wszystkie metody nic nie robić i zwróci null. Jeśli chcesz tylko zastąpić niektóre metody, drugi parametr do getMock jest tablicą metod do przesłonięcia.

$mock = $this->getMock('Class_To_Mock', array('insert', 'update')); 

utworzy mock wystąpienie Class_To_Mock z insert i update funkcji usunięte, gotowe do wartości powrotne być określona.

Ta informacja znajduje się w phpunit docs.

Uwaga, this answer pokazuje więcej przykładów kodu do tej pory, dla wersji phpunit rozpoczynających 5,4

1

Nie sądzę PHPUnit obsługuje częściowe mocks dla testowanego systemu. Jeśli próbujesz wyizolować metody, to jestem pewien, że twoja implementacja działa - ja też to zrobiłem.

Jednak staram się tego uniknąć, z kilku powodów.

Po pierwsze, wiąże test bardzo ściśle z wewnętrzną implementacją klasy. Czy naprawdę dbasz o to, czy została wywołana metoda o nazwie doesLongRunningThing, czy ważniejsze jest to, że "LongRunningThing" zostało zrobione?

Po drugie, kiedy wpadam na to, zawsze zastanawiam się, czy mam jedną klasę wykonującą pracę dwóch osób. Może być konieczna refaktoryzacja klasy dla klasy. Testowanie staje się znacznie łatwiejsze, jeśli doesLongRunningThing() staje się własną klasą, nawet za pomocą jednej metody.

Uważam, że rozwiązaniem jest wstrzyknięcie usług, od których zależy SUT (http://en.wikipedia.org/wiki/Dependency_injection). To sprawia, że ​​implementacja DoesLongRunningThing jest bardziej testowalna.

Bez skoków do interfejsów, oto co zrobię:

class DoesLongRunningThing { 
    public function execute() { 
     sleep(10); 
     return "stuff"; 
    } 
} 

class StuffDoer { 
    protected $doesLongRunningThing; 

    public function setLongRunningThinger(DoesLongRunningThing $obj) { 
     $this->doesLongRunningThing = $obj; 
    } 

    public function doStuff() { 
     return $this->doesLongRunningThing->execute(); 
    } 
} 

Teraz łatwo jest fałszywa:

class StuffDoerTestCase extends PHPUnit_Framework_TestCase { 
    public function testStuffDoer() { 
     $dlrtMock = $this->getMock('DoesLongRunningThing'); 
     $dlrtMock->expects($this->any())->will($this->returnValue("test stuff")); 

     $sd = new StuffDoer(); 
     $sd->setLongRunningThinger($dlrtMock); 
     $result = $sd->doStuff(); 
     $this->assertEquals($result, "test stuff"); 
    } 
} 
+0

Myślę, że PHPUnit jest tylko narzędziem i nie dba o testowany system (jest to umowa użytkownika narzędzia). Według odpowiedzi Brentona PHPUnit _supports_ "partial mocks". Rozpoczynając po pierwszym zdaniu, twoja odpowiedź jest idealna (+1 za to). –

8

PHPUnit_Framework_TestCase::getMock is deprecated since phpunit 5.4. Możemy zamiast tego użyć setMethods.

setMethods (array $ methods) można wywołać w obiekcie Mock Builder, aby określić metody, które mają zostać zastąpione konfigurowalnym podwójnym testem. Zachowanie innych metod nie ulega zmianie. Jeśli wywołasz metodę setMethods (null), żadna metoda nie zostanie zastąpiona.

https://phpunit.de/manual/current/en/test-doubles.html

$observer = $this->getMockBuilder(Observer::class) 
       ->setMethods(['update']) 
       ->getMock(); 

Należy zauważyć, że powyższe getMock jest PHPUnit_Framework_MockObject_MockBuilder::getMock. (phpunit5.6)