Test-Driven Development in JavaScript

309 views

Published on

Slides for a talk I gave to my team at work, in order to convince them that TDD is one of the best things they could do to improve their code.

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
309
On SlideShare
0
From Embeds
0
Number of Embeds
12
Actions
Shares
0
Downloads
4
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Test-Driven Development in JavaScript

  1. 1. Test-Driven Development turn development on its head Aaron Nordyke Sr. Software Engineer Innovations Development
  2. 2. If I were beingcompletely honest,there’s been aconsistent pattern inmy projects.
  3. 3. Dirty CodeZero Unit TestsLow Test CoverageStupid BugsFear Of Breaking Anything
  4. 4. Dirty Code
  5. 5. Dirty Codefunction fillXigrisRelative(){ try { var at = _g("relativeTable"); //var docfrag = document.createDocumentFragment(); Big functions //INR > 3.0 - PTT > 40 sec //INR ea = getEvents("sep_inr_ec", SEPSISREPLY); for (i = 0; i < ea.length; i++) { for (j = 0; j < SEPSISREPLY.VALUES[ea[i]].EVENTS.length; j++) { event_dt = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENTDTDISP; disp = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENT_DISP; rslt = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENT_RESULT; xigrisNotify(_g("inrptt_notify"), [disp, rslt, event_dt]); if (SEPSISREPLY.VALUES[ea[i]].EVENTS[j].RSLTVAL > 3.0) { _g("rel_inrptt_form").ynu[0].checked = true; } } } //PTT ea = getEvents("sep_ptt_ec", SEPSISREPLY); for (i = 0; i < ea.length; i++) { for (j = 0; j < SEPSISREPLY.VALUES[ea[i]].EVENTS.length; j++) { event_dt = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENTDTDISP; disp = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENT_DISP; rslt = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENT_RESULT; xigrisNotify(_g("inrptt_notify"), [disp, rslt, event_dt]); if (SEPSISREPLY.VALUES[ea[i]].EVENTS[j].RSLTVAL > 40) { _g("rel_inrptt_form").ynu[0].checked = true; } } }…
  6. 6. Dirty Code//Platelet Count ea = getEvents("sep_plt_ec", SEPSISREPLY); for (i = 0; i < ea.length; i++) { for (j = 0; j < SEPSISREPLY.VALUES[ea[i]].EVENTS.length; j++) { event_dt = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENTDTDISP; disp = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENT_DISP; …that keep going rslt = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENT_RESULT; xigrisNotify(_g("platelet_notify"), [disp, rslt, event_dt]); if (SEPSISREPLY.VALUES[ea[i]].EVENTS[j].RSLTVAL < 30000) { _g("rel_platelet_form").ynu[0].checked = true; } } } //Gastro-intestinal bleed ea = getEvents("sep_gi_hem_dx", SEPSISREPLY); for (i = 0; i < ea.length; i++) { disp = SEPSISREPLY.VALUES[ea[i]].VALEVENTFTX; rslt = SEPSISREPLY.VALUES[ea[i]].VALEVENTNOMDISP; event_dt = SEPSISREPLY.VALUES[ea[i]].VALEVENTMPVAL; xigrisNotify(_g("gastro_notify"), [disp,rslt,event_dt]); //_g("rel_gastro_form").ynu[0].checked = true; } //Thrombolytic therapy ea = getEvents("sep_thrombolytics_adm", SEPSISREPLY); for (i = 0; i < ea.length; i++) { for (j = 0; j < SEPSISREPLY.VALUES[ea[i]].EVENTS.length; j++) { event_dt = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENTDTDISP; disp = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENT_DISP; rslt = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENT_RESULT; xigrisNotify(_g("thrombo_notify"), [disp, rslt, "Dose Given: " + event_dt]); _g("rel_thrombo_form").ynu[0].checked = true; } } …
  7. 7. Dirty Code//Oral anticoagulants or... ea = getEvents("sep_oral_anticoag_adm", SEPSISREPLY); for (i = 0; i < ea.length; i++) { for (j = 0; j < SEPSISREPLY.VALUES[ea[i]].EVENTS.length; j++) { event_dt = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENTDTDISP; disp = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENT_DISP; rslt = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENT_RESULT; xigrisNotify(_g("antiglyco_notify"), [disp, rslt, "Dose Given: " + event_dt]); _g("rel_antiglyco_form").ynu[0].checked = true; } } …and going //...glycoprotein ea = getEvents("sep_glyco_plt_inh_adm", SEPSISREPLY); for (i = 0; i < ea.length; i++) { for (j = 0; j < SEPSISREPLY.VALUES[ea[i]].EVENTS.length; j++) { event_dt = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENTDTDISP; disp = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENT_DISP; rslt = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENT_RESULT; xigrisNotify(_g("antiglyco_notify"), [disp, rslt, "Dose Given: " + event_dt]); _g("rel_antiglyco_form").ynu[0].checked = true; } } //Aspirin or... ea = getEvents("sep_aspirin_adm", SEPSISREPLY); for (i = 0; i < ea.length; i++) { for (j = 0; j < SEPSISREPLY.VALUES[ea[i]].EVENTS.length; j++) { event_dt = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENTDTDISP; disp = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENT_DISP; rslt = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENT_RESULT; xigrisNotify(_g("aspirin_notify"), [disp, rslt, "Dose Given: " + event_dt]); //TODO - less than 24 hours if (SEPSISREPLY.VALUES[ea[i]].EVENTS[j].RSLTVAL > 650) { _g("rel_aspirin_form").ynu[0].checked = true;
  8. 8. Dirty Code} } } //...other platelet inhibitor ea = getEvents("sep_plt_adm", SEPSISREPLY); for (i = 0; i < ea.length; i++) { for (j = 0; j < SEPSISREPLY.VALUES[ea[i]].EVENTS.length; j++) { event_dt = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENTDTDISP; disp = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENT_DISP; rslt = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENT_RESULT; xigrisNotify(_g("aspirin_notify"), ["Active Order", disp]); _g("rel_aspirin_form").ynu[0].checked = true; } } //Ischemic Stroke ea = getEvents("sep_ischemic_stk_dx", SEPSISREPLY); …and going for (i = 0; i < ea.length; i++) { disp = SEPSISREPLY.VALUES[ea[i]].VALEVENTFTX; rslt = SEPSISREPLY.VALUES[ea[i]].VALEVENTNOMDISP; event_dt = SEPSISREPLY.VALUES[ea[i]].VALEVENTMPVAL; xigrisNotify(_g("stroke_notify"), [disp,rslt,event_dt]); //_g("rel_stroke_form").ynu[0].checked = true; } //Intracranial Arteriovenous Malformation or aneurysm ea = getEvents("sep_aneurysm_dx", SEPSISREPLY); for (i = 0; i < ea.length; i++) { disp = SEPSISREPLY.VALUES[ea[i]].VALEVENTFTX; rslt = SEPSISREPLY.VALUES[ea[i]].VALEVENTNOMDISP; event_dt = SEPSISREPLY.VALUES[ea[i]].VALEVENTMPVAL; xigrisNotify(_g("aneurysm_notify"), [disp,rslt,event_dt]); _g("rel_aneurysm_form").ynu[0].checked = true; }
  9. 9. Dirty Code //Chronic Severe Hepatic Disease //...Hepatic ea = getEvents("sep_hepatic_dis_dx", SEPSISREPLY); for (i = 0; i < ea.length; i++) { disp = SEPSISREPLY.VALUES[ea[i]].VALEVENTFTX; rslt = SEPSISREPLY.VALUES[ea[i]].VALEVENTNOMDISP; event_dt = SEPSISREPLY.VALUES[ea[i]].VALEVENTMPVAL; xigrisNotify(_g("hepatic_notify"), [disp,rslt,event_dt]); _g("rel_hepatic_form").ynu[0].checked = true; } //...ALT > 100 ea = getEvents("sep_alt_ec", SEPSISREPLY); for (i = 0; i < ea.length; i++) { for (j = 0; j < SEPSISREPLY.VALUES[ea[i]].EVENTS.length; j++) { event_dt = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENTDTDISP; disp = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENT_DISP; rslt = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENT_RESULT; xigrisNotify(_g("hepatic_notify"), [disp, rslt, event_dt]); if (SEPSISREPLY.VALUES[ea[i]].EVENTS[j].RSLTVAL > 100) { _g("rel_hepatic_form").ynu[0].checked = true; } } …and going } //...AST > 100 ea = getEvents("sep_ast_ec", SEPSISREPLY); for (i = 0; i < ea.length; i++) { for (j = 0; j < SEPSISREPLY.VALUES[ea[i]].EVENTS.length; j++) { event_dt = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENTDTDISP; disp = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENT_DISP; rslt = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENT_RESULT; xigrisNotify(_g("hepatic_notify"), [disp, rslt, event_dt]);
  10. 10. Dirty Codeif (SEPSISREPLY.VALUES[ea[i]].EVENTS[j].RSLTVAL > 100) { _g("rel_hepatic_form").ynu[0].checked = true; } } } //...AST ea = getEvents("sep_bilirubin_ec", SEPSISREPLY); for (i = 0; i < ea.length; i++) { for (j = 0; j < SEPSISREPLY.VALUES[ea[i]].EVENTS.length; j++) { event_dt = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENTDTDISP; disp = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENT_DISP; rslt = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENT_RESULT; xigrisNotify(_g("hepatic_notify"), [disp, rslt, event_dt]); if (SEPSISREPLY.VALUES[ea[i]].EVENTS[j].RSLTVAL > 2) { _g("rel_hepatic_form").ynu[0].checked = true; } } } //Pregnant or Breastfeeding ea = getEvents("sep_pregnant_ec", SEPSISREPLY); for (i = 0; i < ea.length; i++) { for (j = 0; j < SEPSISREPLY.VALUES[ea[i]].EVENTS.length; j++) { event_dt = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENTDTDISP; disp = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENT_DISP; rslt = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENT_RESULT; xigrisNotify(_g("pregnant_notify"), [disp, rslt, event_dt]); } _g("rel_pregnant_form").ynu[0].checked = true; …and going } ea = getEvents("sep_breastfeeding_ec", SEPSISREPLY); for (i = 0; i < ea.length; i++) {
  11. 11. Dirty Codefor (j = 0; j < SEPSISREPLY.VALUES[ea[i]].EVENTS.length; j++) { event_dt = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENTDTDISP; disp = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENT_DISP; rslt = SEPSISREPLY.VALUES[ea[i]].EVENTS[j].EVENT_RESULT; xigrisNotify(_g("pregnant_notify"), [disp, rslt, event_dt]); _g("rel_pregnant_form").ynu[0].checked = true; } } //check with handler xigrisRelativeHandler(); } catch (e) { errorHandler(e, "fillXigrisRelative()"); }} …and going
  12. 12. Dirty Code Big functions A function should do one thing. “One thing. Just one thing. You stick to that and the rest dont mean sh*t.” Curly, City Slickers
  13. 13. Dirty Code High coupling
  14. 14. Dirty CodeZero Unit Tests
  15. 15. Zero Unit Tests Dirty Code is hard to test
  16. 16. Zero Unit TestsEase ofUnit Testing Function Size
  17. 17. Zero Unit TestsEase ofUnit Testing Coupling
  18. 18. Zero Unit TestsEase ofUnit Testing Number of things the function does
  19. 19. Dirty CodeZero Unit TestsLow Test Coverage
  20. 20. Low Test Coverage I relied on a little helper who was never meant to have so much responsibility.
  21. 21. Low Test Coverage Functional Tests King of the Black Boxes
  22. 22. Low Test Coverage Functional Testing has its place A-Bomb Testing, Nevada Test Site, 1955
  23. 23. Dirty CodeZero Unit TestsLow Test CoverageStupid Bugs
  24. 24. Stupid Bugs “How in the hell did I miss that one?” - Me, after every defect
  25. 25. Dirty CodeZero Unit TestsLow Test CoverageStupid BugsFear Of Breaking Anything
  26. 26. Fear of Breaking Anything
  27. 27. Conventional Development write code
  28. 28. Conventional Development test code
  29. 29. Conventional Development What if we reversed it? write code test code
  30. 30. Conventional Development What if we reversed it? test code write code (WTF?)
  31. 31. Test-Driven Development turn development on its head
  32. 32. Test-Driven Development “Red, Green, Refactor”
  33. 33. Test-Driven Development 1 Write a test that fails Red 3 Clean up code Refactor Green 2 Make the test pass
  34. 34. a (very) trivial example
  35. 35. 1. Write a test that fails 1. Write one unit test addNumber_test.js 2. Run the test and verify the failure
  36. 36. 2. Make the test pass 1. Write just enough code to make failing test pass. addNumber_test.js addNumber.js 2. Run the test and verify the success
  37. 37. 3. Clean up code If able, clean up Code and Unit Tests “Leave the campground cleaner than you found it.” Boy Scouts of America
  38. 38. 1. Write a test that fails (Round 2) 1. Write one unit test addNumber_test.js 2. Run the test and verify the failure
  39. 39. 2. Make the test pass (Round 2) 1. Write just enough code to make failing test pass. addNumber_test.js addNumber.js 2. Run the test and verify the success
  40. 40. 3. Clean up code (Round 2) It’s tough to clean code this simple, so we’re done. addNumber_test.js addNumber.js
  41. 41. Dirty CodeZero Unit TestsLow Test CoverageStupid BugsFear Of Breaking Anything
  42. 42. Clean CodeLotsa Unit TestsHigh Test CoverageStupid Bugs (but a whole lot less of them)No Fear Of Breaking Anything
  43. 43. Clean Code Having to test a little code at a time forced me to write smaller functions with lower coupling that did one thing.
  44. 44. Lotsa Unit Tests They help me sleep at night
  45. 45. High Test CoverageThe code follows the tests.It makes sense that test coverage would be high.
  46. 46. High Test CoverageI no longer have to rely on the functionaltesters to tell me my code works. I knowit works.For my F5 tells me so.
  47. 47. No fear of breaking anythingThis one’s my favorite, because itmeans I can refactor mercilessly.Every time I make a code change, I runthe tests. They tell me immediatelyand loudly if I broke anything.
  48. 48. No fear of breaking anything Like Patrick Bateman in American Psycho, I laugh maniacally while I carve up bodies of code.
  49. 49. Other Reasons that TDD Rules Unit Tests are Documentation
  50. 50. Other Reasons that TDD Rules Closes the Feedback Loop “The act of writing a unit test is more an act of design than of verification. It is also more an act of documentation than of verification. The act of writing a unit test closes a remarkable number of feedback loops, the least of which is the one pertaining to verification of function.” Robert “Uncle Bob” Martin Agile Software Development ( You know code works after 30 seconds, not 10 minutes. )
  51. 51. It can be quite a struggle at first.Downright painful.
  52. 52. No code until you write your tests!
  53. 53. Not a silver bullet
  54. 54. Questions?

×