Slide 1

Slide 1 text

TDD 101 6. Test doubles: the motion picture

Slide 2

Slide 2 text

Test doubles: 
 the motion picture

Slide 3

Slide 3 text

The plot

Slide 4

Slide 4 text

You want to test an object that uses collaborators…

Slide 5

Slide 5 text

You need to isolate its behaviour from that of its collaborators

Slide 6

Slide 6 text

Then, you’ll need… test doubles

Slide 7

Slide 7 text

The argument

Slide 8

Slide 8 text

A unit of software Can be a query: returns a response Can be a command: produces a change in the state of the system You should test the results of the behavior

Slide 9

Slide 9 text

When you test it You want to prove that the behavior: • Is produced by the code of the unit of software • Is not influenced by any other factor

Slide 10

Slide 10 text

So, you need to control the behavior of collaborators Cancelling: no behavior at all Controlling: knowing exactly what they are doing (by means of programming it)

Slide 11

Slide 11 text

The cast

Slide 12

Slide 12 text

Dummy No behavior Only interface

Slide 13

Slide 13 text

namespace Dojo\HappyBirthday\Logger; interface SimpleLogger { public function log(string $channel, string $message): void; }

Slide 14

Slide 14 text

namespace Tests\Dojo\HappyBirthday\Double; use Dojo\HappyBirthday\Logger\SimpleLogger; class LoggerDummy implements SimpleLogger { public function log(string $channel, string $message): void { } }

Slide 15

Slide 15 text

namespace Tests\Dojo\HappyBirthday; use Dojo\HappyBirthday\SendGreetings; use PHPUnit\Framework\TestCase; class SendGreetingsTest extends TestCase { public function testSendGreetingToCustomersWhenIsTheirBirthday() { $clockService = $this->getClockServiceStub(); $logger = $this->getLoggerDummy(); $customerRepository = $this->getCustomerRepositoryStub(); $mailer = $this->getMailerSpy([ new Customer('[email protected]'), new Customer('[email protected]'), new Customer('[email protected]') ]); $sendGreetings = new SendGreetings( $clockService, $customerRepository, $mailer, $logger ); $sendGreetings->execute(); $this->assertEquals(3. $mailer->getMessagesSent()); } }

Slide 16

Slide 16 text

Stub Programmed behavior

Slide 17

Slide 17 text

namespace Dojo\HappyBirthday\Clock; use DateTimeImmutable; interface ClockService { public function currentDate(): DateTimeImmutable; }

Slide 18

Slide 18 text

namespace Tests\Dojo\HappyBirthday\Double; use DateTimeImmutable; use Dojo\HappyBirthday\Clock\ClockService; class ClockServiceStub implements ClockService { /** @var DateTimeImmutable */ private $dateTime; public function __construct(DateTimeImmutable $dateTime) { $this->dateTime = $dateTime; } public function currentDate() : DateTimeImmutable { return $this->dateTime; } }

Slide 19

Slide 19 text

namespace Tests\Dojo\HappyBirthday; use Dojo\HappyBirthday\SendGreetings; use PHPUnit\Framework\TestCase; class SendGreetingsTest extends TestCase { public function testSendGreetingToCustomersWhenIsTheirBirthday() { $clockService = $this->getClockServiceStub(); $logger = $this->getLoggerDummy(); $customerRepository = $this->getCustomerRepositoryStub(); $mailer = $this->getMailerSpy([ new Customer('[email protected]'), new Customer('[email protected]'), new Customer('[email protected]') ]); $sendGreetings = new SendGreetings( $clockService, $customerRepository, $mailer, $logger ); $sendGreetings->execute(); $this->assertEquals(3. $mailer->getMessagesSent()); } }

Slide 20

Slide 20 text

Fake Behavior implementation… but best suited for testing Needs its own tests

Slide 21

Slide 21 text

namespace Dojo\HappyBirthday\Contacts; interface ContactRepositoryInterface { public function retrieveById(UUid $uuid): Contact; }

Slide 22

Slide 22 text

namespace Dojo\HappyBirthday\Contacts; class InMemoryContactRepository implements ContactRepository { public function retrieveById(UUid $uuid): Contact { } }

Slide 23

Slide 23 text

Spy Registers how is used Fragility (coupling)

Slide 24

Slide 24 text

interface Mailer { public function send(Message $message) : void; }

Slide 25

Slide 25 text

class MailerSpy implements Mailer { private $calls = 0; public function send(Message $message) : void { $this->calls++; } public function getCalls() { return $this->calls; } }

Slide 26

Slide 26 text

Mock Has expectations about how is used Takes assertions
 away from the test Fragility (coupling)

Slide 27

Slide 27 text

Taxonomy

Slide 28

Slide 28 text

Behavior Knowledge

Slide 29

Slide 29 text

test doubles by behavior dummy stub fake no behavior fixed complete Applies to any kind of double by knowledge

Slide 30

Slide 30 text

test doubles by knowledge passive spy mock Applies to any kind of double by behavior knows
 nothing registers
 use has
 expectations

Slide 31

Slide 31 text

How to

Slide 32

Slide 32 text

i

Slide 33

Slide 33 text

Design principles Dem SRP OCP DIP ISP LSP DRY Yagni

Slide 34

Slide 34 text

The “real” object Should have: • No behavior • No side effects • Immutability

Slide 35

Slide 35 text

Self-shunt Exploratory and sparse use Simple interface

Slide 36

Slide 36 text

class ServiceTest extends TestCase implements Mailer { private $mailerCalls = 0; public function testMailer() { $sut = new Service($this); $sut->execute(); $this->assertEquals(2, $this->getCalls()); } public function send(Message $message) : void { $this->mailerCalls++; } }

Slide 37

Slide 37 text

Anonymous class Exploratory Single use Simple interface No side effects

Slide 38

Slide 38 text

class ServiceTest extends TestCase { public function testMailer() { $mailer = new class implements Mailer { private $calls = 0; public function send(Message $message) : void { $this->calls++; } puablic function getCalls() { return $this->calls; } }; $sut = new Service($mailer); $sut->execute(); $this->assertEquals(2, $mailer->getCalls()); } }

Slide 39

Slide 39 text

Hand made double Simple interfaces Simple behaviour Could need its
 own tests

Slide 40

Slide 40 text

Mocking library Large interfaces Complex behaviour Multiple scenarios

Slide 41

Slide 41 text

class ServiceTest extends TestCase { public function testMailer() { $mailer = $this->createMock(Mailer::class); $mailer->expects($this->once()) ->method(‘send’) ->with($this->isInstanceOf(Message::class)) ->willReturn(true); $sut = new Service($mailer); $sut->execute(); } }

Slide 42

Slide 42 text

No content