test-driven development in javascript

Post on 03-Jul-2015

162 Views

Category:

Technology

1 Downloads

Preview:

Click to see full reader

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.

TRANSCRIPT

Test-Driven Development turn development on its head

Aaron Nordyke Sr. Software Engineer

Innovations Development

If I were being

completely honest,

there’s been a

consistent pattern in

my projects.

Stupid Bugs

Low Test Coverage

Dirty Code

Fear Of Breaking Anything

Zero Unit Tests

Dirty Code

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

//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

//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

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

//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

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

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

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

High coupling

Dirty Code

Dirty Code

Zero Unit Tests

Zero Unit Tests

Dirty Code is hard to test

Ease of

Unit Testing

Function Size

Zero Unit Tests

Zero Unit Tests

Coupling

Ease of

Unit Testing

Zero Unit Tests

Number of things the

function does

Ease of

Unit Testing

Low Test Coverage

Dirty Code

Zero Unit Tests

Low Test Coverage

I relied on a little helper

who was never meant

to have so much

responsibility.

Low Test Coverage

Functional Tests

King of the Black Boxes

Low Test Coverage

Functional Testing

has its place

A-Bomb Testing, Nevada Test Site, 1955

Stupid Bugs

Low Test Coverage

Dirty Code

Zero Unit Tests

Stupid Bugs

“How in the hell

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

Stupid Bugs

Low Test Coverage

Dirty Code

Fear Of Breaking Anything

Zero Unit Tests

Fear of Breaking Anything

code write

Conventional Development

Conventional Development

code test

Conventional Development

What if we reversed it?

code write code test

Conventional Development

What if we reversed it?

code test code write

(WTF?)

Test-Driven Development turn development on its head

Test-Driven Development

“Red, Green, Refactor”

Red

Green Refactor

Test-Driven Development

Write a test that fails

Make the test pass

1

2

Clean up code 3

a (very) trivial example

1. Write a test that fails

1. Write one unit test

2. Run the test and verify the failure

addNumber_test.js

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

3. Clean up code

If able, clean up Code and Unit Tests

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

1. Write a test that fails

1. Write one unit test

2. Run the test and verify the failure

addNumber_test.js

(Round 2)

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)

3. Clean up code

It’s tough to clean code this simple, so

we’re done.

addNumber.js

addNumber_test.js

(Round 2)

Stupid Bugs

Low Test Coverage

Dirty Code

Fear Of Breaking Anything

Zero Unit Tests

Stupid Bugs (but a whole lot less of them)

High Test Coverage

Clean Code

No Fear Of Breaking Anything

Lotsa Unit Tests

Clean Code

smaller functions

Having to test a little

code at a time forced

me to write

with lower coupling

that did one thing.

Lotsa Unit Tests

They help me sleep at night

High Test Coverage

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

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.

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.

No fear of breaking anything

Like Patrick Bateman

in American Psycho, I

laugh maniacally while

I carve up bodies of

code.

Other Reasons that TDD Rules

Unit Tests are Documentation

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. ( )

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

No code until you write your tests!

Not a silver bullet

Questions?

top related