Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

How to Start Test-Driven Development in Legacy Code

1,078 views

Published on

From The New York XP & Agile Meetup Group presentation on May 17, 2012.

http://www.meetup.com/xp-26/events/16214945/

Published in: Technology
  • Be the first to comment

How to Start Test-Driven Development in Legacy Code

  1. 1. How to Start Test-DrivenDevelopment in Legacy Code The New York XP & Agile Meetup May 17, 2012 Daniel Wellman Director of Technology Tweet #legacytdd #xpnyc
  2. 2. A Quick Survey
  3. 3. “red, green, refactor” by Török Gábor
  4. 4. Yay, TDD!
  5. 5. Boo, my project!
  6. 6. Get to Safety
  7. 7. Be Persistent
  8. 8. Photo by the Dorothy Peyton Gray Transportation Library and Archive at the Los Angeles County Metropolitan Transportation Authority
  9. 9. “Another flaw in the humancharacter is that everybodywants to build and nobodywants to do Kurt Vonnegutmaintenance.” Hocus Pocus
  10. 10. Photo by Fraser Speirs
  11. 11. Get to Safety
  12. 12. TDD Cycle Red Green Refactor
  13. 13. What makes codehard to test?
  14. 14. Highly Coupled Code
  15. 15. Smaller Code
  16. 16. How do I test this?
  17. 17. SeamsPhoto by Art Crimes a.k.a. Susan M. Coles
  18. 18. Legacy TDD Cycle Introduce Seams Red Green Refactor
  19. 19. ??
  20. 20. Break dependencies just enoughto get tests in place
  21. 21. Use Safe Refactoring Tools (if you have them)
  22. 22. Or move very slowly and carefully
  23. 23. A FewTechniques
  24. 24. The Challenge of ‘new’
  25. 25. public class TestCase { public void run() { TestResult result = new TestResult(); try { setUp(); runTest(result); } catch (Exception e) { result.addFailure(e, this); } tearDown(); }}
  26. 26. public class TestCase { public void run() { TestResult result = new TestResult(); try { setUp(); runTest(result); } catch (Exception e) { result.addFailure(e, this); } tearDown(); }}
  27. 27. public class TestCase { public void run() { TestResult result = new TestResult(); try { setUp(); runTest(result); } catch (Exception e) { result.addFailure(e, this); } tearDown(); @Test }} public void exceptionsReportAsFailures() { TestCase testCase = new TestCase(); testCase.run(); // ...?? }
  28. 28. After Extract Parameterpublic class TestCase { public void run(TestResult testResult) { TestResult result = testResult; try { setUp(); runTest(result); } catch (Exception e) { result.addFailure(e, this); } tearDown(); }}
  29. 29. After Extract Parameterpublic class TestCase { public void run(TestResult testResult) { TestResult result = testResult; try { setUp(); runTest(result); @Test } catch (Exception e) { result.addFailure(e, this); public void exceptionsReportAsFailures() { } TestCase testCase = new TestCase(); tearDown(); TestResult result = new TestResult(); } testCase.run(result);} // Make assertions on result }
  30. 30. The Dangerous “Live Wire”
  31. 31. public class MailingListDispatcher { private MailService mailService; public MailingListDispatcher(Status status) { this.mailService = new SMTPMailService("smtp.gmail.com"); // ... } public Result send(Message message) { // ... mailService.send(message); // ... }}
  32. 32. public class MailingListDispatcher { private MailService mailService; public MailingListDispatcher(Status status) { this.mailService = new SMTPMailService("smtp.gmail.com"); // ... } public Result send(Message message) { // ... mailService.send(message); @Test // ... public void testSendSuccessful() { } MailingListDispatcher dispatch =} new MailingListDispatcher(OK); dispatch.send(new Message()); // ... ??? }
  33. 33. After Parameterize Constructorpublic class MailingListDispatcher { private MailService mailService; public MailingListDispatcher( MailService service, Status status) { this.mailService = service; // ... } public Result send(Message message) { // ... mailService.send(message); // ... }}
  34. 34. After Parameterize Constructorpublic class MailingListDispatcher { private MailService mailService; public MailingListDispatcher( MailService service, Status status) { this.mailService = service; // ... } public Result send(Message message) { @Test public// ... void testSendSuccessful() throws Exception { MailService mailService = new FakeMailService(); mailService.send(message); MailingListDispatcher dispatch = // ... } new MailingListDispatcher(mailService, OK); dispatch.send(new Message()); // Inspect the FakeMailService} }
  35. 35. Can’t Make a Fake?
  36. 36. public class SMTPMailServer { public void helo(String hostname) {} public void from(String address) {} public void size(long byteCount) {} public void data() {} public void send(Message message) {} public void quit() {} ... private int sendBytes(byte[] toSend) {} ...}
  37. 37. Override Methodspublic class FakeOverrideSMTPMailServer extends SMTPMailServer { public byte[] sent; // public! @Override protected int sendBytes(byte[] toSend) { // Save the bytes for later inspection // ... return 0; // Whatever... }}
  38. 38. Override Methodspublic class FakeOverrideSMTPMailServer extends SMTPMailServer { public byte[] sent; // public! @Override protected int sendBytes(byte[] toSend) { // Save the bytes for later inspection // ... return 0; // Whatever...public class SMTPMailServer { }} // ... protected int sendBytes(byte[] toSend) { } ...}
  39. 39. Extract Interfacepublic interface MailServer { public void helo(String hostname); public void send(Message message); public void quit();}
  40. 40. Extract Interface sh -ipublic interface MailServer { public void helo(String hostname); public void send(Message message); public void quit();}
  41. 41. Extract Interface sh -i public interface MailServer { public void helo(String hostname); public void send(Message message); public void quit(); }public class SMTPMailServer implements MailServer { // Retro-fitted production...}
  42. 42. Extract Interface sh -i public interface MailServer { public void helo(String hostname); public void send(Message message); public void quit(); }public class SMTPMailServer implements MailServer { // Retro-fitted production...}public class FakeMailServer implements MailServer { // Testing ...}
  43. 43. Long Method?
  44. 44. public Duration vacationRemaining(Date today) { Duration remaining; // ... // ... // Calculate hours worked // ... // ... // How much vacation do they get? // ... // ... // Is it a leap year? // ... // ... return remaining;}
  45. 45. Extract Methodspublic Time hoursWorked(Time start, Time end) { // ...}public int vacationDaysAllowed(EmployeeType type) { // ...}public boolean isLeapMonth(Date date) { // ...}
  46. 46. Not much time to change?
  47. 47. Sprout Method and Sprout Class “sprouting bean” photo by Zoë Jackson
  48. 48. public class EmployeeTable { private List<Employee> employees; public String table() { StringBuilder html = new StringBuilder(); html.append("<table>"); for (Employee employee : employees) { html.append("<tr>"); // Print employee data ... html.append("</tr>"); } html.append("</table>"); return html.toString(); }}
  49. 49. public class EmployeeTable { private List<Employee> employees; public String table() { StringBuilder html = new StringBuilder(); html.append("<table>"); for (Employee employee : employees) { html.append("<tr>"); // Print employee data ... html.append("</tr>"); TODO: } Show count html.append("</table>"); of full-time return html.toString(); } employees}
  50. 50. Sprout Classpublic class FullTimeEmployeeCount { private List<Employee> employees; public FullTimeEmployeeCount( List<Employee> employees) { this.employees = employees; } public int count() { int count = 0; for (Employee employee : employees) { if (employee.isFullTime()) count++; } return count; }}
  51. 51. public String table() { StringBuilder html = new StringBuilder(); html.append("<table>"); for (Employee employee : employees) { html.append("<tr>"); // Print employee data ... html.append("</tr>"); } html.append("<tr>"); html.append("<td colspan=3>"); html.append( new FullTimeEmployeeCount(employees).count()); html.append("</td>"); html.append("</tr>"); html.append("</table>"); return html.toString();}
  52. 52. CrazyThird-Party API?
  53. 53. You arenot alone
  54. 54. Google
  55. 55. End-to-End Tests?
  56. 56. Be Persistent
  57. 57. It’sgoingto behard
  58. 58. 12 9 6 3 0Iteration 20 Iteration 21 Iteration 22 Iteration 23 Iteration 24 Velocity
  59. 59. What ifmanagementdoesn’t think TDD is valuable?
  60. 60. Any amountis an improvement over nothing
  61. 61. Many small stepsadd up over time
  62. 62. Do it every day
  63. 63. Don’t be afraid todelete and reshape
  64. 64. Be Creative
  65. 65. Pair Programming
  66. 66. Spread experts around
  67. 67. Get to SafetyBe Persistent
  68. 68. Yay, TDDon my project!
  69. 69. Upcoming Event “How to Hire and Retain Developers” by Debbie Madden, EVP Monday @ 6 PM General Assembly http://generalassemb.ly/education/More upcoming NYC and Boston Cyrus events at http://www.cyrusinnovation.com/about/events
  70. 70. Questions? Discussion! Twitter: @wellmanE-mail: dwellman@cyrusinnovation.com www.cyrusinnovation.com Twitter: @cyrusinnovation

×