Mocking Demystified

15.745 visualizaciones

Publicado el

Replacing dependents with doubles is a central part of testing that every developer has to master. This talk goes over the different types of doubles and explains their place in testing, how to implement them in a mainstream mocking framework, and which strategies or doubles to use in different message exchange scenarios between objects. After this talk you will have moved a step forward in your understanding of testing in the context of object oriented programming.

Publicado en: Tecnología

Mocking Demystified

  1. Mocking Demystified by @_md
  2. how many of you write tests?
  3. how manywrite tests before the code?
  4. how many of youknow what a mock is?
  5. how many of you use mocks?
  6. ALL YOU NEED TO KNOW ABOUT TESTING (in 5 minutes)
  7. testArrange Act Assert
  8. test instantiateArrange tested object Act Assert
  9. instantiate Arrange tested object$parser = new MarkdownParser;
  10. testArrange run the method Act you want to test Assert
  11. run the method Act you want to test$parser = new MarkdownParser;$html = $parser->toHtml(Hello World);
  12. testArrange Act Assert specify the expected outcome
  13. specify the Assert expected outcome$parser = new MarkdownParser;$html = $parser->toHtml(Hello World);assertTrue(<p>Hello World</p> == $html);
  14. testArrange instantiate tested object Act run the method Assert check expected outcome
  15. $this->toHtml(Hello World) ->shouldReturn(<p>Hello World</p>);
  16. $this->toHtml(Hello World) ->shouldReturn(<p>Hello World</p>); a test is an executable example of a class expected behaviour
  17. READY TO MOCK AROUND?
  18. why?
  19. class ParserSubject{ public function notify(Event $event) { foreach ($this->subscribers as $subscriber) { $subscriber->onChange($event); } }} HOW DO I KNOW NOTIFY WORKS?
  20. class ParserSubjectTest extends TestCase{ /** @test */ function it_notifies_subscribers() { // arrange $parser = new ParserSubject; // act // I need an event!!! $parser->notify(/* $event ??? */); // assert // how do I know subscriber::onChange got called? }} HOW DO I KNOW NOTIFY WORKS?
  21. class EndOfListListener extends EventListener{ public function onNewLine(Event $event) { $html = $event->getText(); if ($this->document->getNextLine() == "") { $html .= "</li></ul>"; } return $html; }} HOW DO I CONTROL EVENT AND DOCUMENT
  22. { focus on unit expensive to instantiate why? control on collaborators’ state indirect outputs undesirable side effects[Meszaros 2007]
  23. a mock is just 1 ofmany test double patterns
  24. doubles are replacement of anything that is not the tested object
  25. { dummy fake doubles stub mock spy[Meszaros 2007]
  26. { dummy no behaviour fake control indirect output doubles stub mock check indirect output spy[Meszaros 2007]
  27. a mockist TDD, London school, approach:only the tested object is real
  28. DOUBLES WITHOUT BEHAVIOUR
  29. dummyhttp://wicker123.deviantart.com/art/Slappy-The-Dummy-148136425
  30. testArrange Act Assert
  31. arrange { instantiate (may require collaborators)
  32. class MarkdownParser{ private $eventDispatcher; public function __construct(EventDispatcher $dispatcher) { $this->eventDispatcher = $dispatcher; }} USE DOUBLES TO BYPASS TYPE HINTING
  33. class MarkdownParser{ private $eventDispatcher; public function __construct(EventDispatcher $dispatcher) { $this->eventDispatcher = $dispatcher; }} USE DOUBLES TO BYPASS TYPE HINTING
  34. class MarkdownParserTest extends TestCase{ function setUp() { $dispatcher = $this->getMock(EventDispatcher); $this->parser = new MardownParser($dispatcher); }} XUNIT EXAMPLE
  35. class MarkdownParser extends ObjectBehavior{ function let($dispatcher) { $dispatcher->beAMockOf(EventDispatcher); $this->beConstructedWith($dispatcher); }} PHPSPEC EXAMPLE
  36. http://wicker123.deviantart.com/art/Slappy-The-Dummy-148136425 “Dummy is a placeholder passed to the SUT (tested object), but never used”[Meszaros 2007]
  37. dummy: double with no behaviourhttp://wicker123.deviantart.com/art/Slappy-The-Dummy-148136425
  38. DOUBLES TO CONTROL INDIRECT OUPUT
  39. arrange { instantiate (may require collaborators) further change state
  40. class MarkdownParserTest extends TestCase{ function setUp() { $dispatcher = $this->getMock(EventDispatcher); $this->parser = new MardownParser($dispatcher); $this->parser->setEncoding(UTF-8); }} FURTHER CHANGE TO THE STATE IN ARRANGE
  41. class MarkdownParserTest extends TestCase{ function setUp() { $dispatcher = $this->getMock(EventDispatcher); $this->parser = new MardownParser($dispatcher); $this->parser->setEncoding(UTF-8); }} FURTHER CHANGE TO THE STATE IN ARRANGE
  42. arrange { instantiate (may require collaborators) further change state configure indirect output
  43. class EndOfListListener extends EventListener{ public function onNewLine(Event $event) { $html = $event->getText(); if ($this->document->getNextLine() == "") { $html .= "</li></ul>"; } return $html; }} INDIRECT OUTPUTS
  44. class EndOfListListener extends EventListener{ public function onNewLine(Event $event) { $html = $event->getText(); if ($this->document->getNextLine() == "") { $html .= "</li></ul>"; } return $html; }} INDIRECT OUTPUTS
  45. fakestub doubles for controlling indirect output http://www.flickr.com/photos/fcharlton/1841638596/ http://www.flickr.com/photos/64749744@N00/476724045
  46. $event->getText(); // will return “Hello World”$this->document->getNextLine(); // will return “” STUBBING: CONTROLLING DOUBLES INDIRECT OUTPUTS
  47. function let($event, $document){ $event->beAMockOf("Event"); $document->beAMockOf("Document"); $this->beConstructedWith($document);} IN PHPSPEC
  48. function let($event, $document){ $event->beAMockOf("Event"); $document->beAMockOf("Document"); $this->beConstructedWith($document);}function it_ends_list_when_next_is_empty($event, $document){ $event->getText()->willReturn("Some text"); $document->getNextLine()->willReturn("");} IN PHPSPEC
  49. function let($event, $document){ $event->beAMockOf("Event"); $document->beAMockOf("Document"); $this->beConstructedWith($document);}function it_ends_list_when_next_is_empty($event, $document){ $event->getText()->willReturn("Some text"); $document->getNextLine()->willReturn(""); $this->onNewLine($event) ->shouldReturn("Some text</li></ul>");} IN PHPSPEC
  50. class EndOfListListener extends EventListener{ public function onNewLine(Event $event) { $html = $event->getText(); if ($this->document->getNextLine() == "") { $html .= "</li></ul>"; } return $html; }} THE CODE AGAIN
  51. function let($event, $document){ $event->beAMockOf("Event"); $document->beAMockOf("Document"); $this->beConstructedWith($document);}function it_ends_list_when_next_is_empty($event, $document){ $event->getText()->willReturn("Some text"); $document->getNextLine()->willReturn(""); $this->onNewLine($event) ->shouldReturn("Some text</li></ul>");} THE SPEC AGAIN
  52. double behaviourloose demand returns null to any method callfake returns null to defined set methodsstub returns value defined or raise error
  53. DOUBLES TO SPEC MESSAGE EXCHANGE
  54. we said beforea test is an executable example of a class expected behaviour
  55. what is behaviour?
  56. B = I + O behaviour = input + output
  57. what can happen in a method?
  58. { return a value modify statemethods print something throw an exception delegate
  59. { return a value not the final modify state behaviourmethods print something throw an exception delegate
  60. {methods return a value we should probably print something delegate that too throw an exception delegate
  61. {methods return a value throw an exception delegate
  62. method strategyreturns a valuethrows an exceptiondelegates
  63. method strategyreturns a value assertions/throws an exception expectationsdelegates mocks & spies
  64. Viewpoints Research Institute Source - Bonnie Macbird URL -http://www.vpri.org“The key in making great and growable systems is much more to design how its modules communicate rather than what their internal properties and behaviours should be.” Messaging
  65. messaging
  66. yeah, but what does it mean?
  67. $this->person->getCar()->getEngine()->ignite();
  68. Inappropriate Intimacy
  69. $this->person->startCar();
  70. tell, don’t ask![Sharp 1997]
  71. exposing lower level implementation makes the code rigid
  72. $this->person->getCar()->getEngine()->ignite();
  73. focus on messagingmakes the code flexible
  74. $this->person->startCar();
  75. mockspy doubles for messaging http://www.flickr.com/photos/scribe215/3234974208/ http://www.flickr.com/photos/21560098@N06/5772919201/
  76. class ParserSubject{ public function notify(Event $event) { foreach ($this->subscribers as $subscriber) { $subscriber->onChange($event); } }} HOW DO I KNOW NOTIFY WORKS?
  77. class ParserSubject{ public function notify(Event $event) { foreach ($this->subscribers as $subscriber) { $subscriber->onChange($event); } }} HOW DO I KNOW NOTIFY WORKS?
  78. use mocks to describe how yourmethod will impact collaborators
  79. use PHPUnit_Framework_TestCase as TestCase;class ParserSubjectTest extends TestCase{ /** @test */ function it_notifies_subscribers() { $event = $this->getMock("Event"); $subscriber = $this->getMock("Subscriber"); $subscriber->expects($this->once()) ->method("onChange") ->with($event); $parser = new ParserSubject(); $parser->attach($subscriber); $parser->notify($event); }} USING PHPUNIT NATIVE MOCK CONSTRUCT
  80. public function testUpdateWithEqualTypes(){ $installer = $this->createInstallerMock(); $manager = new InstallationManager(vendor); $manager->addInstaller($installer); $initial = $this->createPackageMock(); $target = $this->createPackageMock(); $operation = new UpdateOperation($initial, $target, test); $initial ->expects($this->once()) ->method(getType) ->will($this->returnValue(library)); $target ->expects($this->once()) ->method(getType) ->will($this->returnValue(library)); $installer ->expects($this->once()) ->method(supports) ->with(library) ->will($this->returnValue(true)); $installer ->expects($this->once()) ->method(update) ->with($this->repository, $initial, $target); EXCESSIVE SETUP
  81. github.com/padraicb/mockery
  82. use PHPUnit_Framework_TestCase as TestCase;class ParserSubjectTest extends TestCase{ /** @test */ function it_notifies_subscribers() { $event = Mockery::mock("Event"); $subscriber = Mockery::mock("Subscriber"); $subscriber->shouldReceive("onChange") ->with($event); $parser = new ParserSubject(); $parser->attach($subscriber); $parser->notify($event); }} USING MOCKERY
  83. class ParserSubject extends PHPSpec2ObjectBehavior{ /** * @param Event $event * @param Subscriber $subscriber */ function it_notifies_subscribers($event, $subscriber) { $subscriber->onChange($event)->shouldBeCalled(); $this->attach($subscriber); $this->notify($event); }} SAME EXAMPLE IN PHPSPEC
  84. use spies to describe indirect output expectations, retrospectively
  85. github.com/dancras/doubles
  86. use PHPSpec2ObjectBehavior;class ParserSubject extends ObjectBehavior{ function it_notifies_subscribers() { $event = Doubles::fromClass(Event); $subscriber = Doubles::fromClass(Subscriber); $this->attach($subscriber); $this->notify($event); $subscriber->spy(onChange)->callCount()->shoudBe(1); }} SPY EXAMPLE – WITH “DOUBLES” FRAMEWORK
  87. use PHPSpec2ObjectBehavior;class ParserSubject extends ObjectBehavior{ /** * @param Event $event * @param Subscriber $subscriber */ function it_notifies_subscribers($event, $subscriber) { $this->attach($subscriber); $this->notify($event); $subscriber->onChange($event) ->shouldHaveBeenCalled(); }} SPY EXAMPLE – AVAILABLE WITH PROPHECY INTEGRATION
  88. use ProphecyProphet;class TestCase extends SomeUnitTestingFrameworkTestCase{ function createDoubler($name) { $this->prophet = new ProphecyProphet; return $this->prophet->prophesize($name); } function createDummy($name) { return $this->createDoubler($name)->reveal(); }} INTEGRATING PROPHECY
  89. { dummy no behaviour fake control indirect output doubles stub mock messaging spy[Meszaros 2007]
  90. Marcello Duarte I work hereI contribute here I tweet here @_md
  91. Thank you !
  92. Questions or Comments?want to improve on testing? bit.ly/inviqa-bdd-training

×