Tinker Tailor Stub Mock Spy [ITA]

Tinker Tailor Stub Mock Spy [ITA]

Slides for a talk I gave at the local PHP user group, about the different types of test doubles.

Recording: https://www.youtube.com/watch?v=RqsTSjg6aLI

Dab10c1a74e479063be246cc42d99a20?s=128

Francesco Mosca

April 27, 2017
Tweet

Transcript

  1. TINKER TAILOR STUB MOCK SPY U S O E A

    B U S O D E I C O L L A B O R ATO R I
  2. T I N K E R TA I L O

    R S O L D I E R S P Y Romanzo di John le Carré, 1974 Miniserie con Sir Alec Guinness, 1979 Lungometraggio con Gary Oldman, 2011
  3. CHI SONO? • Francesco Mosca • twitter.com/moscaf • Technical Lead

    @ Doing
  4. DI CHE PARLO? • Cos’è un Test Double • Tipi

    di test double • Dummy, Stub, Fake • Spy e Mock • State vs. Behaviour • Mockists vs. Classicists
  5. COS’È UN TEST DOUBLE? Meszaros uses the term Test Double

    as the generic term for any kind of pretend object used in place of a real object for testing purposes. The name comes from the notion of a Stunt Double in movies. Martin Fowler (che cita Gerard Meszaros da “xUnitTest Patterns”) “
  6. SYSTEM UNDER TEST DEPENDED- ON COMPONENT DEPENDED- ON COMPONENT Direct

    Input Direct Output
  7. SYSTEM UNDER TEST TEST DOUBLE Direct Input TEST DOUBLE Direct

    Output
  8. DUMMY Dummy objects are passed around but never actually used.

    Usually they are just used to fill parameter lists. Martin Fowler (che cita Gerard Meszaros da “xUnitTest Patterns”) “
  9. class SecretAgent { private $interceptedMessages; public function intercept(SecretMessage $message) {

    $this->interceptedMessages[] = $message; } public function getInterceptedMessages() { return $this->interceptedMessages; } }
  10. class DummySecretMessage implements SecretMessage { public function __construct() { }

    } /** @test */ public function it_intercepts_secret_messages() { $aMessage = new DummySecretMessage(); $anotherMessage = new DummySecretMessage(); $sut = new SecretAgent(); $sut->intercept($aMessage); $sut->intercept($anotherMessage); $this->assertCount(2, $sut->getInterceptedMessages()); }
  11. STUB Stubs provide canned answers to calls made during the

    test, usually not responding at all to anything outside what's programmed in for the test. Martin Fowler (che cita Gerard Meszaros da “xUnitTest Patterns”) “
  12. class SecretAgent { // … public function encrypt($message, KeyProvider $keyProvider)

    { $key = $keyProvider->getKey(); $f = function($i) use ($key) { return $key . $i . $key; }; return join(" ", array_map($f, explode(" ", $message))); } // … }
  13. /** @test */ public function it_encrypts_important_messages() { $keyProvider = $this->prophesize('TinkerTailor\KeyProvider');

    $keyProvider->getKey()->willReturn("fo"); $sut = new SecretAgent(); $encryptedMessage = $sut->encrypt("too many secrets", $keyProvider->reveal()); $this->assertEquals("fotoofo fomanyfo fosecretsfo", $encryptedMessage); }
  14. class KeyProviderStub implements KeyProvider { public function getKey() { return

    "fo"; } public function invalidateKey($key) { throw new \Exception("I'm just a poor stub"); } }
  15. FAKE Fake objects actually have working implementations, but usually take

    some shortcut which makes them not suitable for production (an in memory database is a good example). Martin Fowler (che cita Gerard Meszaros da “xUnitTest Patterns”) “
  16. class SecretAgent { private $messageRepository; public function __construct(Repository $messageRepository) {

    $this->messageRepository = $messageRepository; } public function intercept(SecretMessage $message) { $this->messageRepository->add($message); } public function getInterceptedMessages() { return $this->messageRepository->findAll(); } // … }
  17. interface Repository { public function add($item); public function remove($item); public

    function find($id); public function findAll(); }
  18. class InMemoryRepository implements Repository { private $storage = []; public

    function add($item) { $this->storage[] = $item; } public function findAll() { return $this->storage; } public function remove($item) { // ... } public function find($id) { // ... } }
  19. /** @test */ public function it_intercepts_secret_messages() { $aMessage = new

    DummySecretMessage(); $anotherMessage = new DummySecretMessage(); $repository = new InMemoryRepository(); $sut = new SecretAgent($repository); $sut->intercept($aMessage); $sut->intercept($anotherMessage); $this->assertCount(2, $sut->getInterceptedMessages()); }
  20. SPY Spies are stubs that also record some information based

    on how they were called. One form of this might be an email service that records how many messages it was sent. Martin Fowler (che cita Gerard Meszaros da “xUnitTest Patterns”) “
  21. /** @test */ public function it_stores_intercepted_messages() { $aMessage = new

    DummySecretMessage(); $repository = $this->prophesize('TinkerTailor\Repository'); $sut = new SecretAgent($repository->reveal()); $sut->intercept($aMessage); $repository->add($aMessage)->shouldHaveBeenCalled(); }
  22. /** @test */ public function it_stores_intercepted_messages() { $aMessage = new

    DummySecretMessage(); $repository = $this->prophesize('TinkerTailor\Repository'); $sut = new SecretAgent($repository->reveal()); $sut->intercept($aMessage); $repository->add($aMessage)->shouldHaveBeenCalled(); $repository->remove(Argument::any())->shouldHaveBeenCalled(); } # phpunit … No calls have been made that match: Double\Repository\P3->remove(*) but expected at least one.
  23. MOCK Mocks are […] objects pre-programmed with expectations which form

    a specification of the calls they are expected to receive. Martin Fowler (che cita Gerard Meszaros da “xUnitTest Patterns”) “
  24. /** @test */ public function it_should_store_intercepted_messages() { $aMessage = new

    DummySecretMessage(); $repository = $this->prophesize('TinkerTailor\Repository'); $repository->add($aMessage)->shouldBeCalled(); $sut = new SecretAgent($repository->reveal()); $sut->intercept($aMessage); }
  25. /** @test */ public function it_should_store_intercepted_messages() { $aMessage = new

    DummySecretMessage(); $repository = $this->prophesize('TinkerTailor\Repository'); $repository->add($aMessage)->shouldBeCalled(); $supervisor = $this->prophesize('TinkerTailor\Supervisor'); $supervisor->authorize()->shouldBeCalled(); $sut = new SecretAgent($repository->reveal()); $sut->intercept($aMessage); } # phpunit … Some predictions failed: Double\TinkerTailor\Supervisor\P5: No calls have been made that match: Double\TinkerTailor\Supervisor\P5->authorize() but expected at least one.
  26. STATE VS. BEHAVIOUR OR OUTCOME VS. COMMUNICATION • I test

    che utilizzano Stub e Fake verificano solitamente lo stato del System Under Test e dei collaboratori coinvolti. • Non sempre agire sui SUT comporta dei cambiamenti di stato verificabili, può inoltre capitare che si aggiunga codice solo per poter verificare lo stato stesso nel test. • I test che utilizzano Mock e Spy si concentrano sul comportamento del SUT nei confronti dei collaboratori. • Definire i comportamenti attesi prima di agire sui SUT “sovverte” lo schema tradizionale given-when-then o arrange- act-assert, sostituendolo con qualcosa di più simile ad un arrange-expect-act.
  27. DETROIT VS. LONDON OR CLASSICISTS VS. MOCKISTS • Concentrarsi sullo

    stato è più facile quando si hanno chiari i componenti del sistema e i loro comportamenti, e i test double vengono utilizzati spesso solo per gli oggetti “di confine” (che parlano con la rete, con il database, etc.). • Uno stub sostituisce solitamente un componente già implementato, favorendo quindi un approccio “dal basso”. • Concentrarsi sui comportamenti è incoraggiato quando si vogliono sfruttare i test come strumento di design, per costruire gli elementi del sistema a partire da esempi di utilizzo. • In caso di TDD, un mock propone un’ipotesi (o una profezia) su un componente ancora non implementato (need-driven o outside-in).
  28. RIFERIMENTI • Wikipedia - Tinker Tailor Soldier Spy • Martin

    Fowler - Test Double e Mocks Aren't Stubs • Niraj Bhatt - Dummy vs. Stub vs. Spy vs. Fake vs. Mock • Marcelo Duarte - PHP test doubles patterns with prophecy • coderabbi - Making Sense of Test Doubles • Dave Marshall - Mocks Aren't Stubs, Fakes, Dummies or Spies • Konstantin Kudryashov (everzet) - Design How Your Objects Talk Through Mocking • Mockist vs. Classicist TDD • Steve Freeman, Nat Pryce - Growing Object-Oriented Software, Guided by Tests
  29. Le parole sono importanti! Chi parla male, pensa male e

    programma male! Kent Beck “
  30. GRAZIE! D O M A N D E P E

    R L I U G G I O ?