Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

 
Reply to this topicStart new topic
> PHPUnit "zagnieżdżone" mocki
Lion
post 11.02.2016, 15:47:13
Post #1





Grupa: Zarejestrowani
Postów: 148
Pomógł: 14
Dołączył: 23.02.2013

Ostrzeżenie: (0%)
-----


Sprawdzam sobie działanie mocków w PHPUnit 5.2.3. Mam pewien problem z "zagnieżdżonymi" mockami, kod wygląda tak:

  1.  
  2. interface EntityInterface
  3. {
  4.  
  5. public function setAncestor(EntityInterface $parent);
  6.  
  7. public function getAncestor();
  8.  
  9. public function addDescendant(EntityInterface $descendant);
  10.  
  11. public function getName();
  12.  
  13. }
  14.  
  15. abstract class Entity implements EntityInterface
  16. {
  17.  
  18. protected $name;
  19.  
  20. /** @var EntityInterface Ancestor */
  21. protected $ancestor = null;
  22.  
  23. /** @var EntityInterface Descendants */
  24. protected $descendants = array();
  25.  
  26. public function __construct($name)
  27. {
  28. $this->name = $name;
  29. }
  30.  
  31. public function getAncestor()
  32. {
  33. return $this->ancestor;
  34. }
  35.  
  36. public function setAncestor(EntityInterface $ancestor)
  37. {
  38. $this->ancestor = $ancestor;
  39. }
  40.  
  41. public function addDescendant(EntityInterface $descendant)
  42. {
  43. $descendant->setAncestor($this);
  44. $this->descendants[$descendant->getName()] = $descendant;
  45. }
  46.  
  47. public function getName()
  48. {
  49. return $this->name;
  50. }
  51.  
  52. }
  53.  


Test wygląda tak:

  1.  
  2. class EntityTest extends PHPUnit_Framework_TestCase
  3. {
  4.  
  5. public function testEntityDescendantCanBeSet()
  6. {
  7. $entity = $this->getMockBuilder(Entity::class)
  8. ->setMethods(array('addDescendant'))
  9. ->disableOriginalConstructor()
  10. ->getMock();
  11. $descendant_entity = $this->getMockBuilder(Entity::class)
  12. ->setMethods(array('getName', 'setAncestor'))
  13. ->disableOriginalConstructor()
  14. ->getMock();
  15. $entity->expects($this->once())
  16. ->method('addDescendant');
  17. $descendant_entity->expects($this->once())
  18. ->method('setAncestor');
  19. $descendant_entity->expects($this->once())
  20. ->method('getName')
  21. ->will($this->returnValue('entity_name'));
  22.  
  23. $entity->addDescendant($descendant_entity);
  24. }
  25.  
  26. }
  27.  


Dostaję taki komunikat:

Cytat
1) EntityTest::testEntityDescendantCanBeSet
Expectation failed for method name is equal to <string:setAncestor> when invoked 1 time(s).
Method was expected to be called 1 times, actually called 0 times.


Podobny komunikat jest dla getName jeśli test setAncestor jest zakomentowany. Co jest nie tak i jak to można przetestować?


--------------------
Go to the top of the page
+Quote Post
Crozin
post 11.02.2016, 16:55:30
Post #2





Grupa: Zarejestrowani
Postów: 6 476
Pomógł: 1306
Dołączył: 6.08.2006
Skąd: Kraków

Ostrzeżenie: (0%)
-----


Utworzyłeś mocka, więc wywołanie metody addDescendant nie robi nic - nie wywołuje Twojego kodu. I tutaj należy zauważyć, że źle podchodzisz do tematu:

1. W miarę możliwości staraj się unikać tworzenia mocków.
2. Skoro testujesz tutaj klasę Entity, a jest on abstrakcyjna utwórz sobie nic nie robiącą jej implementację (class DummyEntity extends Entity { }) i korzystaj z jej obiektów.
3. Testuj kontrakt klasy, a nie jej implementację. Czasami faktycznie istotne jest to ile razy dana metoda została wywołana itp. itd., ale tutaj to nie ma znaczenia. To co w tym kodzie jest istotne to to czy:
3.1. Konstruktor faktycznie ustawia nazwę klasy zwracaną przez getName().
  1. // arrange & act
  2. $entity = new DummyEntity('name');
  3.  
  4. // assert
  5. $this->assertEquals('name', $entity->getName());

3.2. addDescendant() dodaje obiekt jako dziecko i ustawia temu dziecku rodzica na siebie samego.
  1. // arrange
  2. $parent = new DummyEntity('parent');
  3. $child = new DummyEntity('child');
  4.  
  5. // act
  6. $parent->addDescedant($child);
  7.  
  8. // assert
  9. $this->assertCount(1, $parent->getDescendants());
  10. $this->assertArrayHasKey('child', $parent->getDescendants());
  11. $this->assertSame($parent, $child->getParent());

Co Cię interesuje ile razy getName() było wywołane? Jakie to ma znaczenie tutaj?
Go to the top of the page
+Quote Post
lukaskolista
post 19.02.2016, 08:21:46
Post #3





Grupa: Zarejestrowani
Postów: 872
Pomógł: 94
Dołączył: 31.03.2010

Ostrzeżenie: (0%)
-----


W php7 z pomocą przychodzą klasy anonimowe, np.
  1. abstract class AbstractEntityAbc {}
  2.  
  3. class AbstractEntityAbcTest
  4. {
  5. public function testFunctionX()
  6. {
  7. $abc = new class('arg1', 'arg2') extends AbstractEntityAbc {/* Tutaj możesz nadpisać metody lub zaimplementować metody abstrakcyjne */};
  8. $this->assertAbc($abc->functionX());
  9. }
  10. }


Edit:
To samo dotyczy klas nieabstrakcyjnych, zamiast tworzyć mocki możesz tworzyć klasy anonimowe, które po nich dziedziczą.

Ten post edytował lukaskolista 19.02.2016, 08:23:40
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
1 Użytkowników czyta ten temat (1 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Wersja Lo-Fi Aktualny czas: 27.04.2024 - 19:43