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
550
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
360
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
240
Other Decks in Programming
See All in Programming
AI時代に設計が 最大の生産性レバーになる 意図駆動開発とデータを消さない設計|Don't Delete Your Data or Your Intent — Design as the Deepest Lever in the AI Era
tomohisa
1
510
FDEが実現するAI駆動経営の現在地
gonta
2
250
全PRの83%がAIレビューだけでマージできるようになった開発組織はその後どうなったか
athug
1
1.3k
AI Engineeringは、AIプロダクトだけのものか? 〜AIがソフトウェアを作る時代の新しい当たり前〜 / No AI in your product. AI Engineering in your development.
rkaga
4
350
アルゴリズムは何を圧縮しているのか ─ Haskell から育った「圧縮代数」というメンタルモデル
naoya
16
3.8k
ソフトウェア設計に溶けるインフラ ― AWS CDK のインフラ認識論
konokenj
3
740
霧の中の代数的エフェクト
funnyycat
1
450
そこに3びきプロダクトがいるじゃろう——生成AI時代における“価値が届かない理由”の構造
kosuket
0
390
torikago - Ruby::Boxで照らすモジュラモノリスの実行境界
se4weed
1
320
生成AIで帳票OCRが「簡単に」作れる時代になった?
kon_shou
0
310
AWS CDK を「作」ってみた 〜フルスクラッチで見えた CDK の裏側〜 / aws-cdk-from-scratch
gotok365
3
2.8k
信頼性について考えてみる(SRE NEXT 2026 miniLT)
hayama17
0
240
Featured
See All Featured
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
47
8.2k
Learning to Love Humans: Emotional Interface Design
aarron
275
41k
ラッコキーワード サービス紹介資料
rakko
1
4.2M
Stewardship and Sustainability of Urban and Community Forests
pwiseman
0
410
JAMstack: Web Apps at Ludicrous Speed - All Things Open 2022
reverentgeek
1
520
Designing for humans not robots
tammielis
254
26k
The SEO identity crisis: Don't let AI make you average
varn
0
520
Faster Mobile Websites
deanohume
310
32k
Agile Leadership in an Agile Organization
kimpetersen
PRO
0
200
The #1 spot is gone: here's how to win anyway
tamaranovitovic
3
1.1k
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
400
The Illustrated Guide to Node.js - THAT Conference 2024
reverentgeek
1
420
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