55
Test-Driven Development turn development on its head Aaron Nordyke Sr. Software Engineer Innovations Development

Test-Driven Development in JavaScript

Embed Size (px)

DESCRIPTION

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.

Citation preview

Page 1: Test-Driven Development in JavaScript

Test-Driven Development turn development on its head

Aaron Nordyke Sr. Software Engineer

Innovations Development

Page 2: Test-Driven Development in JavaScript

If I were being

completely honest,

there’s been a

consistent pattern in

my projects.

Page 3: Test-Driven Development in JavaScript

Stupid Bugs

Low Test Coverage

Dirty Code

Fear Of Breaking Anything

Zero Unit Tests

Page 4: Test-Driven Development in JavaScript

Dirty Code

Page 5: Test-Driven Development in JavaScript

function fillXigrisRelative(){ try { var at = _g("relativeTable"); //var docfrag = document.createDocumentFragment(); //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; } } } …

Dirty Code

Big functions

Page 6: Test-Driven Development in JavaScript

//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; 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; } } …

…that keep going

Dirty Code

Page 7: Test-Driven Development in JavaScript

//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; } } //...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;

…and going

Dirty Code

Page 8: Test-Driven Development in JavaScript

} } } //...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); 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; }

…and going

Dirty Code

Page 9: Test-Driven Development in JavaScript

//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; } } } //...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]);

…and going

Dirty Code

Page 10: Test-Driven Development in JavaScript

if (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; } } ea = getEvents("sep_breastfeeding_ec", SEPSISREPLY); for (i = 0; i < ea.length; i++) {

…and going

Dirty Code

Page 11: Test-Driven Development in JavaScript

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; } } //check with handler xigrisRelativeHandler(); } catch (e) { errorHandler(e, "fillXigrisRelative()"); } }

…and going

Dirty Code

Page 12: Test-Driven Development in JavaScript

Dirty Code

A function should do one thing.

Big functions

“One thing. Just one thing. You stick to

that and the rest don't mean sh*t.”

Curly, City Slickers

Page 13: Test-Driven Development in JavaScript

High coupling

Dirty Code

Page 14: Test-Driven Development in JavaScript

Dirty Code

Zero Unit Tests

Page 15: Test-Driven Development in JavaScript

Zero Unit Tests

Dirty Code is hard to test

Page 16: Test-Driven Development in JavaScript

Ease of

Unit Testing

Function Size

Zero Unit Tests

Page 17: Test-Driven Development in JavaScript

Zero Unit Tests

Coupling

Ease of

Unit Testing

Page 18: Test-Driven Development in JavaScript

Zero Unit Tests

Number of things the

function does

Ease of

Unit Testing

Page 19: Test-Driven Development in JavaScript

Low Test Coverage

Dirty Code

Zero Unit Tests

Page 20: Test-Driven Development in JavaScript

Low Test Coverage

I relied on a little helper

who was never meant

to have so much

responsibility.

Page 21: Test-Driven Development in JavaScript

Low Test Coverage

Functional Tests

King of the Black Boxes

Page 22: Test-Driven Development in JavaScript

Low Test Coverage

Functional Testing

has its place

A-Bomb Testing, Nevada Test Site, 1955

Page 23: Test-Driven Development in JavaScript

Stupid Bugs

Low Test Coverage

Dirty Code

Zero Unit Tests

Page 24: Test-Driven Development in JavaScript

Stupid Bugs

“How in the hell

did I miss that one?” - Me, after every defect

Page 25: Test-Driven Development in JavaScript

Stupid Bugs

Low Test Coverage

Dirty Code

Fear Of Breaking Anything

Zero Unit Tests

Page 26: Test-Driven Development in JavaScript

Fear of Breaking Anything

Page 27: Test-Driven Development in JavaScript

code write

Conventional Development

Page 28: Test-Driven Development in JavaScript

Conventional Development

code test

Page 29: Test-Driven Development in JavaScript

Conventional Development

What if we reversed it?

code write code test

Page 30: Test-Driven Development in JavaScript

Conventional Development

What if we reversed it?

code test code write

(WTF?)

Page 31: Test-Driven Development in JavaScript

Test-Driven Development turn development on its head

Page 32: Test-Driven Development in JavaScript

Test-Driven Development

“Red, Green, Refactor”

Page 33: Test-Driven Development in JavaScript

Red

Green Refactor

Test-Driven Development

Write a test that fails

Make the test pass

1

2

Clean up code 3

Page 34: Test-Driven Development in JavaScript

a (very) trivial example

Page 35: Test-Driven Development in JavaScript

1. Write a test that fails

1. Write one unit test

2. Run the test and verify the failure

addNumber_test.js

Page 36: Test-Driven Development in JavaScript

2. Make the test pass

1. Write just enough code to make failing test

pass.

2. Run the test and verify the success

addNumber_test.js

addNumber.js

Page 37: Test-Driven Development in JavaScript

3. Clean up code

If able, clean up Code and Unit Tests

“Leave the campground cleaner than you found it.” Boy Scouts of America

Page 38: Test-Driven Development in JavaScript

1. Write a test that fails

1. Write one unit test

2. Run the test and verify the failure

addNumber_test.js

(Round 2)

Page 39: Test-Driven Development in JavaScript

2. Make the test pass

1. Write just enough code to make failing

test pass.

2. Run the test and verify the success

addNumber.js

addNumber_test.js

(Round 2)

Page 40: Test-Driven Development in JavaScript

3. Clean up code

It’s tough to clean code this simple, so

we’re done.

addNumber.js

addNumber_test.js

(Round 2)

Page 41: Test-Driven Development in JavaScript

Stupid Bugs

Low Test Coverage

Dirty Code

Fear Of Breaking Anything

Zero Unit Tests

Page 42: Test-Driven Development in JavaScript

Stupid Bugs (but a whole lot less of them)

High Test Coverage

Clean Code

No Fear Of Breaking Anything

Lotsa Unit Tests

Page 43: Test-Driven Development in JavaScript

Clean Code

smaller functions

Having to test a little

code at a time forced

me to write

with lower coupling

that did one thing.

Page 44: Test-Driven Development in JavaScript

Lotsa Unit Tests

They help me sleep at night

Page 45: Test-Driven Development in JavaScript

High Test Coverage

The code follows the tests. It makes sense that test coverage would be high.

Page 46: Test-Driven Development in JavaScript

High Test Coverage

I no longer have to rely on the functional

testers to tell me my code works. I know

it works.

For my F5 tells me so.

Page 47: Test-Driven Development in JavaScript

No fear of breaking anything

This one’s my favorite, because it

means I can refactor mercilessly.

Every time I make a code change, I run

the tests. They tell me immediately

and loudly if I broke anything.

Page 48: Test-Driven Development in JavaScript

No fear of breaking anything

Like Patrick Bateman

in American Psycho, I

laugh maniacally while

I carve up bodies of

code.

Page 49: Test-Driven Development in JavaScript

Other Reasons that TDD Rules

Unit Tests are Documentation

Page 50: Test-Driven Development in JavaScript

Other Reasons that TDD Rules

“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

Closes the Feedback Loop

You know code works after

30 seconds, not 10 minutes. ( )

Page 51: Test-Driven Development in JavaScript

It can be quite a struggle at first. Downright painful.

Page 52: Test-Driven Development in JavaScript

No code until you write your tests!

Page 53: Test-Driven Development in JavaScript
Page 54: Test-Driven Development in JavaScript

Not a silver bullet

Page 55: Test-Driven Development in JavaScript

Questions?