Test Driven DesignUsing tests and mocks to drive the design of software                                              Attil...
●   End to End      –   Real environment●   Integration                     rd      –   Against 3 party API●   Unit      –...
Feedback                           12                           10  Feedback about Quality                           8    ...
Feedback                                External Quality                           12                           10  Feedba...
Feedback                                External Quality                           12                           10  Feedba...
Feedback                                External Quality                           12                           10  Feedba...
Feedback                                External Quality                           12                           10  Feedba...
Bad design:                                ● Rigidity                                ● Fragility                          ...
Bad design:                                ● Rigidity                                ● Fragility                          ...
Bad design:                                ● Rigidity                                ● Fragility                          ...
End to End vs Unit tests●   End to End:                           ●    Unit tests:    ●   Slow execution and feedback     ...
Pyramid          End to end          Integration             Unit
Unit tests:                 state based testingpublic class Light {           // Test    private boolean on;              ...
Ticket Machineclass TrainTicketMachine implements TicketMachine {    private List<Integer> pressedButtons = new ArrayList<...
Ticket Machine                    Message:                 reserve([1,2,3])                      Ticket Reserver
Ticket Persister  Ticket Machine                                                   DBpublic interface TicketReserver {    ...
Messages=[                             reserve(1,2,3),Ticket Machine               reserve(4,5,6)]                 Has rec...
public class Spy implements TicketReserver {    private List<Integer> received = new ArrayList<Integer>();    @Override   ...
@RunWith(MockitoJUnitRunner.class)public class TrainTickerReserverTest {    TrainTicketMachine ticketMachine;    @Mock Tic...
Test smells
Test smellsclass TrainTicketMachine implements TicketMachine {    private List<Integer> pressedButtons = new ArrayList<Int...
Test smellsclass TrainTicketMachine implements TicketMachine {    private List<Integer> pressedButtons = new ArrayList<Int...
Test smellsclass TrainTicketMachine implements TicketMachine {    private List<Integer> pressedButtons = new ArrayList<Int...
Test smellsclass TrainTicketMachine implements TicketMachine {    private List<Integer> pressedButtons = new ArrayList<Int...
Solutionclass TrainTicketMachine implements TicketMachine {    private List<Integer> pressedButtons = new ArrayList<Intege...
Copy component
Dependency inversion
The Dependency Inversion Principle, Robert C. Martin, C++ Report, May 1996
Test smellsPrivate method access
Test smellsPrivate method access
Test smells   Private method accessPrinciples of Object Oriented Design, Robert C. Martin, 2003
Test smellsExcessive test setup
Test smells Excessive test setup          •  •   SRP violation• Too much coupling    • (No isolation)
Test smellsDifficult mocking (deep stubbing)
Test smells      Difficult mocking       (deep stubbing)•    Law of demeter violation        Northeastern University, 1987
Test smellsDifficult mockingdog.getTail().wag()
Test smells   Difficult mockingdog.expressHappiness()
Test smellsAccessing local variable
Test smellsAccessing local variable    •SRP violation •   Method is too long
Test smellsDupplication in test and production
Test smellsDupplication in test and production        •Missing abstraction   •     Mocking 3rd party components
rd                           3 party API mockingpublic class Greetings {    [...]    public void greetUsers() throws SQLEx...
rd                           3 party API mockingpublic class Greetings {    [...]    public void greetUsers() throws SQLEx...
Test smellsToo many changes in test code
Test smellsToo many changes in test code  Meyer, Bertrand (1988). Object-Oriented Software Construction.
Test smellsToo many dependencies
Test smellsToo many dependencies    ● SRP violation ● Missing abstraction
Test smellsDifficult to instantiate SUT
Test smellsDifficult to instantiate SUT●   Hidden dependency (e.g.: Singleton)     ●   Insufficient domain separation
rd                                                                         3rd party  3 party                             ...
Application wiring                                                       rd                                               ...
Application wiring                                                      rd                                                ...
Application wiring                                                       rd                                               ...
Application wiring                              End to end test             rd                                            ...
TDD
How does it work?          A●   Write a failing test    for A
How does it work?                   Interface discovery          A                              B●   Write a failing test ...
How does it work?                   Interface discovery          A                              B●   Write a failing test ...
How does it work?                   Interface discovery                Interface discovery          A                     ...
Benefits●   Early design feedback (SRP, DIP, OCP, etc..)●   Mocks encourage the use of „Tell Dont Ask” principle    → Well...
DEMOCashier service:●   Reads barcodes●   Queries prices (REST)●   Prints receipts●   Commands:     –   „Command: NewSale”...
Jersey/ApacheCommandListener                                                                                              ...
References●   Growing Object-Oriented Software Guided by Tests     ●   Steve Freeman, Nat Pryce●   Why You Dont Get Mock O...
Upcoming SlideShare
Loading in …5
×

Using tests and mocks to drive the design of software

996 views

Published on

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
996
On SlideShare
0
From Embeds
0
Number of Embeds
8
Actions
Shares
0
Downloads
7
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Using tests and mocks to drive the design of software

  1. 1. Test Driven DesignUsing tests and mocks to drive the design of software Attila Magyar Microsec Plc 2012
  2. 2. ● End to End – Real environment● Integration rd – Against 3 party API● Unit – Isolated
  3. 3. Feedback 12 10 Feedback about Quality 8 6 4 2 0 Unit test Integration test End to end testGrowing object-oriented software guided by tests: Steve Freeman, Nat Pryce
  4. 4. Feedback External Quality 12 10 Feedback about Quality 8 6 4 2 0 Unit test Integration test End to end testGrowing object-oriented software guided by tests: Steve Freeman, Nat Pryce
  5. 5. Feedback External Quality 12 10 Feedback about Quality 8 6 4 2 0 Unit test Integration test End to end testGrowing object-oriented software guided by tests: Steve Freeman, Nat Pryce
  6. 6. Feedback External Quality 12 10 Feedback about Quality 8 6 4 2 0 Unit test Integration test End to end testGrowing object-oriented software guided by tests: Steve Freeman, Nat Pryce
  7. 7. Feedback External Quality 12 10 Feedback about Quality 8 6 4 2 0 Unit test Integration test End to end testGrowing object-oriented software guided by tests: Steve Freeman, Nat Pryce
  8. 8. Bad design: ● Rigidity ● Fragility ● Immobility Feedback External Quality 12 Code Quality 10 Feedback about Quality 8 6 4 2 0 Unit test Integration test End to end testGrowing object-oriented software guided by tests: Steve Freeman, Nat Pryce
  9. 9. Bad design: ● Rigidity ● Fragility ● Immobility Feedback External Quality 12 Code Quality 10 Feedback about Quality 8 6 4 2 0 Unit test Integration test End to end testGrowing object-oriented software guided by tests: Steve Freeman, Nat Pryce
  10. 10. Bad design: ● Rigidity ● Fragility ● Immobility Feedback External Quality 12 Code Quality 10 Feedback about Quality 8 6 4 2 0 Unit test Integration test End to end testGrowing object-oriented software guided by tests: Steve Freeman, Nat Pryce
  11. 11. End to End vs Unit tests● End to End: ● Unit tests: ● Slow execution and feedback ● Fast execution and feedback ● Sometimes unreliable ● Always deterministic ● Difficult and time-consuming to ● Easy to write and automate write ● Easy to localize bugs ● Difficult to localize bugs ● Verifies basic correctness ● Verifies configuration, integration, environment
  12. 12. Pyramid End to end Integration Unit
  13. 13. Unit tests: state based testingpublic class Light { // Test private boolean on; light.switchOn(); public void switchOn(){ on = true; assertTrue(light.isOn()); } public void switchOff(){ on = false; light.switchOff(); } public boolean isOn(){ assertFalse(light.isOn()) return on; }}
  14. 14. Ticket Machineclass TrainTicketMachine implements TicketMachine { private List<Integer> pressedButtons = new ArrayList<Integer>(); [...] public void press(int buttonNumber) { pressedButtons.add(buttonNumber); } public void enter() { ticketReserver.reserve(new ArrayList<Integer>(pressedButtons)); }}
  15. 15. Ticket Machine Message: reserve([1,2,3]) Ticket Reserver
  16. 16. Ticket Persister Ticket Machine DBpublic interface TicketReserver { void reserve(List<Integer> ticketCode);} Ticket Reserver
  17. 17. Messages=[ reserve(1,2,3),Ticket Machine reserve(4,5,6)] Has received 123? Ticket Reserver
  18. 18. public class Spy implements TicketReserver { private List<Integer> received = new ArrayList<Integer>(); @Override public void reserve(List<Integer> codes) { received.addAll(codes); } public void assertReceived(List<Integer> codes) { assertThat(received, equalTo(codes)); }}@Testpublic void reservesTicketUsingEnteredCodes() { // Given Spy spy = new Spy(); ticketMachine = new TrainTicketMachine(spy); // When ticketMachine.press(1); ticketMachine.press(2); ticketMachine.enter(); // Then spy.assertReceived(Arrays.asList(1, 2));}
  19. 19. @RunWith(MockitoJUnitRunner.class)public class TrainTickerReserverTest { TrainTicketMachine ticketMachine; @Mock TicketReserver reserver; @Test public void reservesTicketUsingEnteredCodes() { // Given ticketMachine = new TrainTicketMachine(reserver); (spyito) // When ticketMachine.press(1); ticketMachine.press(2); ticketMachine.enter(); // Then verify(reserver).reserve(Arrays.asList(1, 2)); }}@RunWith(JMock.class)public class TrainTickerReserverTest { @Mock TicketReserver reserver; Mockery context = new JUnit4Mockery(); TrainTicketMachine ticketMachine; @Test public void reservesTicketUsingEnteredCodes() { context.checking(new Expectations() {{ oneOf(reserver).reserve(Arrays.asList(1, 2)); }}); ticketMachine = new TrainTicketMachine(reserver); ticketMachine.press(1); ticketMachine.press(2); ticketMachine.enter(); }}
  20. 20. Test smells
  21. 21. Test smellsclass TrainTicketMachine implements TicketMachine { private List<Integer> pressedButtons = new ArrayList<Integer>(); public void press(int buttonNumber) { pressedButtons.add(buttonNumber); } public void enter() { TicketReserver.reserve(new ArrayList<Integer>(pressedButtons)); }}
  22. 22. Test smellsclass TrainTicketMachine implements TicketMachine { private List<Integer> pressedButtons = new ArrayList<Integer>(); public void press(int buttonNumber) { pressedButtons.add(buttonNumber); } public void enter() { TicketReserver.reserve(new ArrayList<Integer>(pressedButtons)); }} class TrainTicketMachine implements TicketMachine { private List<Integer> pressedButtons = new ArrayList<Integer>(); public void press(int buttonNumber) { pressedButtons.add(buttonNumber); } public void enter() { TicketReserver.getInstance().reserve(new ArrayList<Integer>(pressedButtons)); } }
  23. 23. Test smellsclass TrainTicketMachine implements TicketMachine { private List<Integer> pressedButtons = new ArrayList<Integer>(); public void press(int buttonNumber) { pressedButtons.add(buttonNumber); } public void enter() { TicketReserver.reserve(new ArrayList<Integer>(pressedButtons)); }} class TrainTicketMachine implements TicketMachine { private List<Integer> pressedButtons = new ArrayList<Integer>(); public void press(int buttonNumber) { pressedButtons.add(buttonNumber); } public void enter() { TicketReserver.getInstance().reserve(new ArrayList<Integer>(pressedButtons)); } } class TrainTicketMachine implements TicketMachine { private List<Integer> pressedButtons = new ArrayList<Integer>(); private TicketReserver ticketReserver = new TicketReserver(); public void press(int buttonNumber) { pressedButtons.add(buttonNumber); } public void enter() { ticketReserver.reserve(new ArrayList<Integer>(pressedButtons)); } }
  24. 24. Test smellsclass TrainTicketMachine implements TicketMachine { private List<Integer> pressedButtons = new ArrayList<Integer>(); public void press(int buttonNumber) { pressedButtons.add(buttonNumber); } public void enter() { TicketReserver.reserve(new ArrayList<Integer>(pressedButtons)); }} class TrainTicketMachine implements TicketMachine { private List<Integer> pressedButtons = new ArrayList<Integer>(); public void press(int buttonNumber) { pressedButtons.add(buttonNumber); } public void enter() { TicketReserver.getInstance().reserve(new ArrayList<Integer>(pressedButtons)); } } class TrainTicketMachine implements TicketMachine { private List<Integer> pressedButtons = new ArrayList<Integer>(); private TicketReserver ticketReserver = new TicketReserver(); public void press(int buttonNumber) { pressedButtons.add(buttonNumber); } public void enter() { ticketReserver.reserve(new ArrayList<Integer>(pressedButtons)); } }
  25. 25. Solutionclass TrainTicketMachine implements TicketMachine { private List<Integer> pressedButtons = new ArrayList<Integer>(); private TicketReserver ticketReserver; public TrainTicketMachine(TicketReserver ticketReserver) { this.ticketReserver = ticketReserver; } public void press(int buttonNumber) { pressedButtons.add(buttonNumber); } public void enter() { ticketReserver.reserve(new ArrayList<Integer>(pressedButtons)); }}
  26. 26. Copy component
  27. 27. Dependency inversion
  28. 28. The Dependency Inversion Principle, Robert C. Martin, C++ Report, May 1996
  29. 29. Test smellsPrivate method access
  30. 30. Test smellsPrivate method access
  31. 31. Test smells Private method accessPrinciples of Object Oriented Design, Robert C. Martin, 2003
  32. 32. Test smellsExcessive test setup
  33. 33. Test smells Excessive test setup • • SRP violation• Too much coupling • (No isolation)
  34. 34. Test smellsDifficult mocking (deep stubbing)
  35. 35. Test smells Difficult mocking (deep stubbing)• Law of demeter violation Northeastern University, 1987
  36. 36. Test smellsDifficult mockingdog.getTail().wag()
  37. 37. Test smells Difficult mockingdog.expressHappiness()
  38. 38. Test smellsAccessing local variable
  39. 39. Test smellsAccessing local variable •SRP violation • Method is too long
  40. 40. Test smellsDupplication in test and production
  41. 41. Test smellsDupplication in test and production •Missing abstraction • Mocking 3rd party components
  42. 42. rd 3 party API mockingpublic class Greetings { [...] public void greetUsers() throws SQLException { Statement stmt = connection.createStatement(); sayHelloTo(stmt.executeQuery("select name from users where type=1 or type=2")); }}@Testpublic void saysHelloToUsers() throws SQLException { when(conn.createStatement()).thenReturn(stmt); when(stmt.executeQuery("select name from users where type=1 or type=2")).thenReturn(users); movies.greetUsers(); [...]}
  43. 43. rd 3 party API mockingpublic class Greetings { [...] public void greetUsers() throws SQLException { Statement stmt = connection.createStatement(); sayHelloTo(stmt.executeQuery("select name from users where type=1 or type=2")); }} Duplication@Testpublic void saysHelloToUsers() throws SQLException { when(conn.createStatement()).thenReturn(stmt); when(stmt.executeQuery("select name from users where type=1 or type=2")).thenReturn(users); movies.greetUsers(); [...]}
  44. 44. Test smellsToo many changes in test code
  45. 45. Test smellsToo many changes in test code Meyer, Bertrand (1988). Object-Oriented Software Construction.
  46. 46. Test smellsToo many dependencies
  47. 47. Test smellsToo many dependencies ● SRP violation ● Missing abstraction
  48. 48. Test smellsDifficult to instantiate SUT
  49. 49. Test smellsDifficult to instantiate SUT● Hidden dependency (e.g.: Singleton) ● Insufficient domain separation
  50. 50. rd 3rd party 3 party Application domain Adapter Adapter msg BL BL Adapter BLDomain of the outside world 3rd partyObject must send messages to it peers in terms of its domain language.
  51. 51. Application wiring rd 3 party - „new” and „set” 3rd party Application domain Adapter Adapter msg BL BL Adapter BL - Main method - Spring/Guice - XML/AnnotationsDomain of the outside world 3rd party
  52. 52. Application wiring rd 3 party - „new” and „set” 3rd party Application domain MOCK Unit test MOCK SUT MOCK Adapter BL - Main method - Spring/Guice - XML/AnnotationsDomain of the outside world 3rd party
  53. 53. Application wiring rd 3 party - „new” and „set” Real Integration Application domain SUT Adapter BL BL Adapter BL - Main method - Spring/Guice - XML/AnnotationsDomain of the outside world 3rd party
  54. 54. Application wiring End to end test rd 3 party - „new” and „set” rd 3 party Application domain Adapter Adapter BL BL Adapter BL - Main method - Spring/Guice - XML/AnnotationsDomain of the outside world 3rd party
  55. 55. TDD
  56. 56. How does it work? A● Write a failing test for A
  57. 57. How does it work? Interface discovery A B● Write a failing test for A● Introduce the interface of a collaborator B
  58. 58. How does it work? Interface discovery A B● Write a failing test for A● Introduce the interface of a collaborator B● Mock the interface● Use the mock to Finish A
  59. 59. How does it work? Interface discovery Interface discovery A B C● Write a failing test ● Write a failing test ● C is an adapter for A for B ● Use integration● Introduce the ● Introduce the test for this interface of a B interface of C● Mock the interface ● Mock the interface● Use the mock to ● "Ensure contract compliance" finish A between A and B ● Use the mock to finish B
  60. 60. Benefits● Early design feedback (SRP, DIP, OCP, etc..)● Mocks encourage the use of „Tell Dont Ask” principle → Well encapsulated code● Outside-In approach ● Simpler interface, (and implementation) ● No dead code● Less debugging● More coverage → ● Higher confidence in code and refactoring ● Less post-release bugs
  61. 61. DEMOCashier service:● Reads barcodes● Queries prices (REST)● Prints receipts● Commands: – „Command: NewSale” – „Command: EndSale” – „Input: barcode=100008888559”
  62. 62. Jersey/ApacheCommandListener Http client Command REST Translator Command Catalog Money Product Product Entered Sale Started Catalog Sale Ended Product java.awt.print e ric tP uc od Barcode pr SaleEventListener Receipt Printer priceCalculated Receiver CashRegister
  63. 63. References● Growing Object-Oriented Software Guided by Tests ● Steve Freeman, Nat Pryce● Why You Dont Get Mock Objects ● Gregory Moeck, rubyconf 2011● Using Mocks And Tests To Design Role-Based Objects ● Isaiah Perumalla● Mock Roles, not Objects ● Steve Freeman, Nat Pryce, Tim Mackinnon, Joe Walnes, High Holborn● The Deep Synergy Between Testability and Good Design ● Michael Feathers● Surely the Mars Rover Needed Integrated Tests! (Maybe Not?) ● J. B. Rainsberger● Joey Devilla SOLID principle posters

×