FAKE IT OUTSIDE-IN TDDDavid Völkel 05.10.2017 @ XP Days Germany
@DAVIDVOELKEL@codecentric
@softwerkskammer
#TDD
INFLUENCES 2003 Kent Beck’s "Fake It" Pattern 2009 #GOOS’s "Outside-In" Design 2013 Emily Bache
"Outside-In development with Double Loop TDD" 2014 Justin Searls "The Failures of 'Intro to TDD'"
COMBINATION 2015 Dimitry Polivaev Outside-In with faked Data 2016 SoCraTes DE Outside-In Fake It Session 2017 Refinement
INFLUENCES 2003 Kent Beck’s "Fake It" Pattern 2009 #GOOS’s "Outside-In" Design 2013 Emily Bache
"Outside-In development with Double Loop TDD" 2014 Justin Searls "The Failures of 'Intro to TDD'"
COMBINATION 2015 Dimitry Polivaev Outside-In with faked Data 2016 SoCraTes DE Outside-In Fake It Session 2017 Refinement
INFLUENCES 2003 Kent Beck’s "Fake It" Pattern 2009 #GOOS’s "Outside-In" Design
COMBINATION FAKE IT OUTSIDE-IN TDD
FAKE IT OUTSIDE-IN TDD
INFLUENCES 2003 Kent Beck’s "Fake It" Pattern 2009 #GOOS’s "Outside-In" Design 2013 Emily Bache
"Outside-In development with Double Loop TDD" 2014 Justin Searls "The Failures of 'Intro to TDD'" 2017 Llewelyn Falco „Extreme: Fake it Till you Make It“
FAKE IT OUTSIDE-IN TDD
WHY? Outside-In => YAGNI free design
WHY? Outside-In => YAGNI free design
Avoid mocking drawbacks - Readability - Refactorability
AGENDA Theory
Building Blocks Fake It Outside-In
Questions
AGENDA Theory
Building Blocks Fake It Outside-In
Questions
OPEN SPACE Practice
Mob Programming Session Reflect
Retro Trade-Offs
BUILDING BLOCKS
INTEGRATION OPERATION SEGREGATION PRINCIPLE
*"Integration Operation Segregation Principle", Ralf Westphal "Die kniffligen Fälle beim Testen - Sichtbarkeit", Stefan Lieser
*
INTEGRATION OPERATION SEGREGATION PRINCIPLE
public void sendDiscountMailingTo(String email) { Customer customer = customerDB.findCustomerBy(email);
Account account = customer.account(); String discount = account == FREE ? "no" : account == BASE ? "a 10%" : account == PREMIUM ? "a 25%" : "-ERROR-"; String content = "Hello " + customer.getName() + ",\n\n" + "This week you get " + discount + " discount " + "on all our products.\n\n" + "Best Regards,\n" + "ACME Customer Service";
mailService.sendMail(email, content); }
OPERATION
public void sendDiscountMailingTo(String email) { Customer customer = customerDB.findCustomerBy(email);
Account account = customer.account(); String discount = account == FREE ? "no" : account == BASE ? "a 10%" : account == PREMIUM ? "a 25%" : "-ERROR-"; String content = "Hello " + customer.getName() + ",\n\n" + "This week you get " + discount + " discount " + "on all our products.\n\n" + "Best Regards,\n" + "ACME Customer Service";
mailService.sendMail(email, content); }
INTEGRATION OPERATION SEGREGATION PRINCIPLE
INTEGRATION
INTEGRATION
OPERATION
public void sendDiscountMailingTo(String email) { Customer customer = customerDB.findCustomerBy(email);
Account account = customer.account(); String discount = account == FREE ? "no" : account == BASE ? "a 10%" : account == PREMIUM ? "a 25%" : "-ERROR-"; String content = "Hello " + customer.getName() + ",\n\n" + "This week you get " + discount + " discount " + "on all our products.\n\n" + "Best Regards,\n" + "ACME Customer Service";
mailService.sendMail(email, content); }
INTEGRATION OPERATION SEGREGATION PRINCIPLE
INTEGRATION
INTEGRATION
OPERATION
public void sendDiscountMailingTo(String email) { Customer customer = customerDB.findCustomerBy(email);
Account account = customer.account(); String discount = account == FREE ? "no" : account == BASE ? "a 10%" : account == PREMIUM ? "a 25%" : "-ERROR-"; String content = "Hello " + customer.getName() + ",\n\n" + "This week you get " + discount + " discount " + "on all our products.\n\n" + "Best Regards,\n" + "ACME Customer Service";
mailService.sendMail(email, content); }
INTEGRATION OPERATION SEGREGATION PRINCIPLE
OPERATION
INTEGRATIONpublic void sendDiscountMailingTo(String email) { Customer customer = customerDB.findCustomerBy(email);
String content = renderMessage(customer, customer.account());
mailService.sendMail(email, content); }
private String renderMessage(Customer customer, Account account) { String discount = account == FREE ? "no" : account == BASE ? "a 10%" : account == PREMIUM ? "a 25%" : "-ERROR-"; return "Hello " + customer.getName() + ",\n\n" + "This week you get " + discount + " discount " + "on all our products.\n\n" + "Best Regards,\n" + "ACME Customer Service"; }
INTEGRATION OPERATION SEGREGATION PRINCIPLE
public void sendDiscountMailingTo(String email) { Customer customer = customerDB.findCustomerBy(email);
String content = renderMessage(customer, customer.account());
mailService.sendMail(email, content); }
private String renderMessage(Customer customer, Account account) { String discount = account == FREE ? "no" : account == BASE ? "a 10%" : account == PREMIUM ? "a 25%" : "-ERROR-"; return "Hello " + customer.getName() + ",\n\n" + "This week you get " + discount + " discount " + "on all our products.\n\n" + "Best Regards,\n" + "ACME Customer Service"; }
TESTS?
OPERATION
public void sendDiscountMailingTo(String email) { Customer customer = customerDB.findCustomerBy(email);
String content = renderMessage(customer, customer.account());
mailService.sendMail(email, content); }
private String renderMessage(Customer customer, Account account) { String discount = account == FREE ? "no" : account == BASE ? "a 10%" : account == PREMIUM ? "a 25%" : "-ERROR-"; return "Hello " + customer.getName() + ",\n\n" + "This week you get " + discount + " discount " + "on all our products.\n\n" + "Best Regards,\n" + "ACME Customer Service"; }
TESTS?
N UNITTESTS
INTEGRATION
OPERATION
public void sendDiscountMailingTo(String email) { Customer customer = customerDB.findCustomerBy(email);
String content = renderMessage(customer, customer.account());
mailService.sendMail(email, content); }
private String renderMessage(Customer customer, Account account) { String discount = account == FREE ? "no" : account == BASE ? "a 10%" : account == PREMIUM ? "a 25%" : "-ERROR-"; return "Hello " + customer.getName() + ",\n\n" + "This week you get " + discount + " discount " + "on all our products.\n\n" + "Best Regards,\n" + "ACME Customer Service"; }
TESTS?1 INTEGRATION TEST
N UNITTESTS
GREEN BAR PATTERNS*
Obvious Implementation
Fake it (until you make it)
Triangulation
* Kent Beck in "TDD by Example"
GREEN BAR PATTERNS
Obvious Implementation
Fake it
Triangulation
Trade-Off
Complexity
GREEN BAR PATTERNS DEMO
SWEET SPOT
Trivial
GREEN BAR PATTERNS
Obvious Implementation
Fake it
Triangulation
SWEET SPOT
Trivial
Structure
GREEN BAR PATTERNS
Obvious Implementation
Fake it
Triangulation
SWEET SPOT
Trivial
Structure
Logic
GREEN BAR PATTERNS
Obvious Implementation
Fake it
Triangulation
PREPARATORY REFACTORINGS*
*"An example of preparatory refactoring", Martin Fowler
PREPARATORY REFACTORINGS*
*"An example of preparatory refactoring", Martin Fowler
"GREEN" PHASE TRIANGULATION
Implementation Run Test
"Green Phase"
LIMIT YOUR TIME IN RED
"Green Phase"
Implementation Run Test
LIMIT YOUR TIME IN RED
"Green Phase"
Implementation Run Test
Implementation Run TestPreparatory Refactoring
PREPARATORY REFACTORINGS
DEMO
Acceptance Test
UI
Persistence
OUTSIDE-IN
Acceptance Test
UI
Persistence
OUTSIDE-IN & MOCKS
Unit test Mock
OUTSIDE-IN & MOCKS
Unit Test
OUTSIDE-IN & MOCKS
Acceptance Test
UI
Persistence
OUTSIDE-IN & MOCKS
Non trivial
Acceptance Test
OUTSIDE-IN & FAKE IT
Comprehensive
Acceptance Test
Faked Result
OUTSIDE-IN & FAKE IT
Acceptance Test
Fake
Fake
OUTSIDE-IN & FAKE IT
Acceptance Test Drive Structure
through Refactoring
OUTSIDE-IN & FAKE IT
OUTSIDE-IN & FAKE IT
Acceptance Test
Unit Test
Fill logic with
triangulation
OUTSIDE-IN & FAKE IT
Acceptance Test Unit Test
OUTSIDE-IN & FAKE IT INTEGRATION
Fake It
TriangulationOPERATION
Start with
• comprehensive Acceptance Test
• faked result
Drive structure by refactoring
Drive logic by unit tests
OUTSIDE-IN & FAKE IT INTEGRATION
Fake It
TriangulationOPERATION
Start with
• comprehensive Acceptance Test
• faked result
Drive structure by refactoring
Drive logic by unit tests
DIAMOND KATA
FAKE IT OUTSIDE-IN
DEMO
FAKE-IT VARIANT CALCULATE BACKWARDS
DEMO
WHY? Outside-In
=> YAGNI free design Without mocking drawbacks
WHY? Outside-In
=> YAGNI free design Without mocking drawbacks
Vs Triangulation More time in GREEN Better Design Guidance
DISCUSSION
LICENSE Creative Commons Attribution-ShareAlike 3.0
IMAGES Most are Public Domain except theses
Creative Commons with attributions:
"Unstruttalbrücke" by Störfix
From State Library of Queensland
FAKE IT OUTSIDE-IN TDD MOB PROGRAMMING SESSION
AGENDA 05 min. Intro & Kata 40 min. Mob Programming 15 min. Retro & Tradeoffs
OUTSIDE-IN & FAKE IT INTEGRATION
Fake It
TriangulationOPERATION
Start with
• comprehensive Acceptance Test
• faked result
Drive structure by refactoring
Drive logic by unit tests
OUTSIDE-IN & FAKE IT INTEGRATION
Fake It
TriangulationOPERATION
Start with
• comprehensive Acceptance Test
• faked result
Drive structure by refactoring
Drive logic by unit tests
DIAMOND KATA
KATA Continue Diamond Kata
https://github.com/davidvoelkel/diamond-kata
CODING SESSION
RETROSPECTIVE Observations?
RETROSPECTIVE Observations?
Strenghts / Weaknesses?
TRADE-OFFS FAKE IT VS
OUTSIDE-IN & MOCKS TRIANGULATION
CONDITIONAL INTERACTIONS
Mocking when IOSP not possible
public String signup(String username) throws Exception { if(userDB.findUserBy(username) == null) { userDB.createUser(new User(username)); return "Welcome " + username; } else { return "Username ' " + username + "' " + "already taken, please choose another"; } }
COUPLING OUTSIDE-IN
Mocking Fake It
Decoupling Refactorability
DATA GUIDES STRUCTURE
Triangulation Fake It
Structure Cumbersome Data guides well
# BRANCHES
Triangulation Fake It
# N 1
More confidence More effort
# BRANCHES
Triangulation Fake It
# N 1
Fake data easy to forget
Omitting cases is tempting
TIME IN GREEN
Triangulation
Prep Refactoring Triangulation
Fake It
DISCUSSION