Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Tests doubles: the motion picture
Search
Fran Iglesias
November 02, 2018
Programming
560
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Tests doubles: the motion picture
Test doubles explained
Fran Iglesias
November 02, 2018
More Decks by Fran Iglesias
See All by Fran Iglesias
Tips for daily refactoring
franiglesias
0
370
Introduction to TDD: Red-Green-Refactor
franiglesias
1
330
Testing value objects
franiglesias
0
290
testing the unpredictable
franiglesias
0
260
Low cost techniques for test doubles
franiglesias
0
250
Other Decks in Programming
See All in Programming
AIに既存システムを理解させる技術 ~レガシーを見捨てないハーネスエンジニアリング入門~
ochtum
0
140
AWS DevOps AgentのAzure接続機能を検証して見えた活用法/Use Cases Verified for the AWS DevOps Agent's Azure Connectivity Feature
masakiokuda
1
270
React本体のコードリーディング
high_g_engineer
1
150
Go 1.27 における memory allocation の高速化
andpad
0
300
運用ダッシュボードの設計を誰も教えてくれないのだけどみなさんどうしてるんですか? - チームに監視するという文化を根付かせるための第一歩を踏みたい -
satoshi256kbyte
1
140
私のClaude Code活用法 (個人開発編) - PHPerKaigi mini #4(2026/08/24)
panda_program
1
140
使いながら育てる Claude Code — 開発フローの1コマンド化 × 繰り返し指摘の自動仕組み化
shiki_kakaku
0
1.9k
GDG Korea Android: 2026 I/O Extended ~ What's new in Android development tools
pluu
0
240
AWS Transform Customによる Spring Boot 2.xから4.xへのVerUp
satoshi256kbyte
2
100
そこに3びきプロダクトがいるじゃろう——生成AI時代における“価値が届かない理由”の構造
kosuket
0
570
承認済みなのに差戻しできてしまうバグ、型で潰せます
shinchit
0
100
170k Jobs a Day on GKE: Scaling Mercari's CI Platform - and What's Next for AI-Native Development
junyaokabe
0
120
Featured
See All Featured
Java REST API Framework Comparison - PWX 2021
mraible
34
9.7k
SEO Brein meetup: CTRL+C is not how to scale international SEO
lindahogenes
1
2.8k
XXLCSS - How to scale CSS and keep your sanity
sugarenia
249
1.3M
Understanding Cognitive Biases in Performance Measurement
bluesmoon
32
3k
Tell your own story through comics
letsgokoyo
1
1k
Digital Projects Gone Horribly Wrong (And the UX Pros Who Still Save the Day) - Dean Schuster
uxyall
1
2.5k
Avoiding the “Bad Training, Faster” Trap in the Age of AI
tmiket
0
210
Measuring & Analyzing Core Web Vitals
bluesmoon
9
970
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
12
1.8k
Visual Storytelling: How to be a Superhuman Communicator
reverentgeek
2
620
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
35
3.8k
Faster Mobile Websites
deanohume
310
32k
Transcript
TDD 101 6. Test doubles: the motion picture
Test doubles: the motion picture
The plot
You want to test an object that uses collaborators…
You need to isolate its behaviour from that of its
collaborators
Then, you’ll need… test doubles
The argument
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
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
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)
The cast
Dummy No behavior Only interface
namespace Dojo\HappyBirthday\Logger; interface SimpleLogger { public function log(string $channel, string
$message): void; }
namespace Tests\Dojo\HappyBirthday\Double; use Dojo\HappyBirthday\Logger\SimpleLogger; class LoggerDummy implements SimpleLogger { public
function log(string $channel, string $message): void { } }
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()); } }
Stub Programmed behavior
namespace Dojo\HappyBirthday\Clock; use DateTimeImmutable; interface ClockService { public function currentDate():
DateTimeImmutable; }
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; } }
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()); } }
Fake Behavior implementation… but best suited for testing Needs its
own tests
namespace Dojo\HappyBirthday\Contacts; interface ContactRepositoryInterface { public function retrieveById(UUid $uuid): Contact;
}
namespace Dojo\HappyBirthday\Contacts; class InMemoryContactRepository implements ContactRepository { public function retrieveById(UUid
$uuid): Contact { } }
Spy Registers how is used Fragility (coupling)
interface Mailer { public function send(Message $message) : void; }
class MailerSpy implements Mailer { private $calls = 0; public
function send(Message $message) : void { $this->calls++; } public function getCalls() { return $this->calls; } }
Mock Has expectations about how is used Takes assertions away
from the test Fragility (coupling)
Taxonomy
Behavior Knowledge
test doubles by behavior dummy stub fake no behavior fixed
complete Applies to any kind of double by knowledge
test doubles by knowledge passive spy mock Applies to any
kind of double by behavior knows nothing registers use has expectations
How to
i
Design principles Dem SRP OCP DIP ISP LSP DRY Yagni
The “real” object Should have: • No behavior • No
side effects • Immutability
Self-shunt Exploratory and sparse use Simple interface
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++; } }
Anonymous class Exploratory Single use Simple interface No side effects
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()); } }
Hand made double Simple interfaces Simple behaviour Could need its
own tests
Mocking library Large interfaces Complex behaviour Multiple scenarios
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(); } }
None