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

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

Francesco Mosca

April 27, 2017
Tweet

More Decks by Francesco Mosca

Other Decks in Technology

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

    View Slide

  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

    View Slide

  3. CHI SONO?
    • Francesco Mosca
    • twitter.com/moscaf
    • Technical Lead @ Doing

    View Slide

  4. DI CHE PARLO?
    • Cos’è un Test Double
    • Tipi di test double
    • Dummy, Stub, Fake
    • Spy e Mock
    • State vs. Behaviour
    • Mockists vs. Classicists

    View Slide

  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”)

    View Slide

  6. SYSTEM
    UNDER
    TEST
    DEPENDED-
    ON
    COMPONENT
    DEPENDED-
    ON
    COMPONENT
    Direct Input
    Direct Output

    View Slide

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

    View Slide

  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”)

    View Slide

  9. class SecretAgent
    {
    private $interceptedMessages;
    public function intercept(SecretMessage $message)
    {
    $this->interceptedMessages[] = $message;
    }
    public function getInterceptedMessages()
    {
    return $this->interceptedMessages;
    }
    }

    View Slide

  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());
    }

    View Slide

  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”)

    View Slide

  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)));
    }
    // …
    }

    View Slide

  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);
    }

    View Slide

  14. class KeyProviderStub implements KeyProvider
    {
    public function getKey()
    {
    return "fo";
    }
    public function invalidateKey($key)
    {
    throw new \Exception("I'm just a poor stub");
    }
    }

    View Slide

  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”)

    View Slide

  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();
    }
    // …
    }

    View Slide

  17. interface Repository
    {
    public function add($item);
    public function remove($item);
    public function find($id);
    public function findAll();
    }

    View Slide

  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) {
    // ...
    }
    }

    View Slide

  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());
    }

    View Slide

  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”)

    View Slide

  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();
    }

    View Slide

  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.

    View Slide

  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”)

    View Slide

  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);
    }

    View Slide

  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.

    View Slide

  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.

    View Slide

  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).

    View Slide

  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

    View Slide

  29. Le parole sono importanti!
    Chi parla male, pensa male e programma male!
    Kent Beck

    View Slide

  30. GRAZIE!
    D O M A N D E P E R L I U G G I O ?

    View Slide