Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Tinker Tailor Stub Mock Spy [EN]

Francesco Mosca
April 27, 2017
91

Tinker Tailor Stub Mock Spy [EN]

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

Francesco Mosca

April 27, 2017
Tweet

Transcript

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

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

    R S O L D I E R S P Y A novel by John le Carré, 1974 A TV mini series with Sir Alec Guinness, 1979 A movie with Gary Oldman, 2011
  3. WHAT WILL I TALK ABOUT? • What’s a Test Double

    • Types of Test Doubles • Dummies, Stubs, Fakes • Spies and Mocks • State vs. Behaviour • Mockists vs. Classicists
  4. WHAT’S A 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 (citing Gerard Meszaros from “xUnitTest Patterns”) “
  5. DUMMY Dummy objects are passed around but never actually used.

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

    $this->interceptedMessages[] = $message; } public function getInterceptedMessages() { return $this->interceptedMessages; } }
  7. 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()); }
  8. 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”) “
  9. 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))); } // … }
  10. /** @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); }
  11. class KeyProviderStub implements KeyProvider { public function getKey() { return

    "fo"; } public function invalidateKey($key) { throw new \Exception("I'm just a poor stub"); } }
  12. 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”) “
  13. 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(); } // … }
  14. 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) { // ... } }
  15. /** @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()); }
  16. 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”) “
  17. /** @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(); }
  18. /** @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.
  19. 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”) “
  20. /** @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); }
  21. /** @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.
  22. STATE VS. BEHAVIOUR OR OUTCOME VS. COMMUNICATION • Tests using

    Stubs and Fakes focus verification usually on the state of the System Under Test and involved collaborators. • Not always an action on a sut SUT imples verifiable state changes, and it may happen that extra code is added only to verify state in tests (eg. getters irrelevant to the business logic). • Tests using Mocks and in a way Spies usually focus on the behaviour of the SUT in relation with its collaborators. • Defining expected behaviours before acting on the SUT “subvers” the traditional given-when-then (or arrange- act-assert) test setup, changing it into something more akin to “arrange-expect- act”.
  23. DETROIT VS. LONDON OR CLASSICISTS VS. MOCKISTS • Focusing on

    state is easier when a system’s components and their behaviour is clear, and test doubles are often involved in “border” objects (those talking to the network, database, and third party code in general). • A stub often replaces an already implemented component, favouring a “bottom-up” design approach. • Focusing on behaviours is encouraged when you want to use tests as a design tool, to build the elements of a system starting from examples of their usage. • In the case of TDD, a mock offers an hypothesis (or a prophecy) about a not- yet-implemented component (need-driven or outside-in design).
  24. REFERENCES • 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
  25. Words matter! Those who speak incorrectly think incorrectly, and code

    incorrectly! Kent Beck Nanni Moretti, kinda. “