unit test your database! (pgcon 2009)

104
Unit Test Your Database! David E. Wheeler PostgreSQL Experts, Inc. PGCon, May 21, 2009 Thursday, May 21, 2009

Upload: postgresql-experts-inc

Post on 14-Nov-2014

398 views

Category:

Technology


4 download

DESCRIPTION

 

TRANSCRIPT

Page 1: Unit Test Your Database! (PgCon 2009)

Unit Test Your Database!

David E. WheelerPostgreSQL Experts, Inc.

PGCon, May 21, 2009

Thursday, May 21, 2009

Page 2: Unit Test Your Database! (PgCon 2009)

Why?Thursday, May 21, 2009

Page 3: Unit Test Your Database! (PgCon 2009)

Do thesesound familiar?

Thursday, May 21, 2009

Page 4: Unit Test Your Database! (PgCon 2009)

“It takes too long to write tests.”

Thursday, May 21, 2009

Page 5: Unit Test Your Database! (PgCon 2009)

“Testing will just slow me down.”

Thursday, May 21, 2009

Page 6: Unit Test Your Database! (PgCon 2009)

“It takes too long to run tests.”

Thursday, May 21, 2009

Page 7: Unit Test Your Database! (PgCon 2009)

“We already write app-level unit tests.”

Thursday, May 21, 2009

Page 8: Unit Test Your Database! (PgCon 2009)

“I test stuff by running my app.”

Thursday, May 21, 2009

Page 9: Unit Test Your Database! (PgCon 2009)

“Tests never find relevant bugs.”

Thursday, May 21, 2009

Page 10: Unit Test Your Database! (PgCon 2009)

“This code is so simple it doesn’t need tests.”

Thursday, May 21, 2009

Page 11: Unit Test Your Database! (PgCon 2009)

“This function is too hard to test.”

Thursday, May 21, 2009

Page 12: Unit Test Your Database! (PgCon 2009)

“This is a private function.”

Thursday, May 21, 2009

Page 13: Unit Test Your Database! (PgCon 2009)

“Tests can't prove a program correct so why bother?”

Thursday, May 21, 2009

Page 14: Unit Test Your Database! (PgCon 2009)

“The behavior of the code changes a lot and rewriting the tests to match will just slow things down.”

Thursday, May 21, 2009

Page 15: Unit Test Your Database! (PgCon 2009)

“If I imagined a problem to write tests for, I probably wrote code that doesn’t have that problem.”

Thursday, May 21, 2009

Page 16: Unit Test Your Database! (PgCon 2009)

“I can produce software that works even without focusing specifically on low- level unit tests.”

Thursday, May 21, 2009

Page 17: Unit Test Your Database! (PgCon 2009)

“I’m lucky enough to only be dealing with really good developers.”

Thursday, May 21, 2009

Page 18: Unit Test Your Database! (PgCon 2009)

“AHHHHHHHHH!!!! NOT TESTING! Anything but testing! Beat me, whip me, send me to Detroit, but don’t make me write tests!”

—Michael Schwern, Test::Tutorial

Thursday, May 21, 2009

Page 19: Unit Test Your Database! (PgCon 2009)

Test ConceptionsFor finding bugs

Difficult

Irrelevant

Time-consuming

For inexperienced developers

Unnecessary for simple code

Best for fragile code

Users test the code

App tests are sufficient

For public interface only

Prove nothing

For stable code

I really like Detroit

Thursday, May 21, 2009

Page 20: Unit Test Your Database! (PgCon 2009)

Let’s Get Real

Thursday, May 21, 2009

Page 21: Unit Test Your Database! (PgCon 2009)

What does it take?

Thursday, May 21, 2009

Page 22: Unit Test Your Database! (PgCon 2009)

Test-Driven Development

Say you need a Fibonacci Calculator

Start with a test

Write the simplest possible function

Add more tests

Update the function

Wash, rinse, repeat…

Thursday, May 21, 2009

Page 23: Unit Test Your Database! (PgCon 2009)

Simple Test

BEGIN;SETsearch_pathTOpublic,tap;SELECT*FROMno_plan();

SELECTcan('{fib}');SELECTcan_ok('fib',ARRAY['integer']);

SELECT*FROMfinish();ROLLBACK;

Thursday, May 21, 2009

Page 24: Unit Test Your Database! (PgCon 2009)

CREATEORREPLACEFUNCTIONfib(fib_forinteger)RETURNSintegerAS$$BEGINRETURN0;END;$$LANGUAGEplpgsql;

Simple Function

Thursday, May 21, 2009

Page 25: Unit Test Your Database! (PgCon 2009)

%psql‐dtry‐ffib.sqlCREATEFUNCTION%pg_prove‐vdtrytest_fib.sqltest_fib.sql..ok1‐Schemapg_catalogorpublicortapcanok2‐Functionfib(integer)shouldexist1..2okAlltestssuccessful.Files=1,Tests=2,0secs(0.03usr+0.00sys=0.03CPU)Result:PASS%❚

%

Run the Test

That was easy

Thursday, May 21, 2009

Page 26: Unit Test Your Database! (PgCon 2009)

Add Assertions

SELECTcan('{fib}');SELECTcan_ok('fib',ARRAY['integer']);SELECTis(fib(0),0,'fib(0)shouldbe0');SELECTis(fib(1),1,'fib(1)shouldbe1');

Thursday, May 21, 2009

Page 27: Unit Test Your Database! (PgCon 2009)

%%psql‐dtry‐ffib.sqlCREATEFUNCTION%pg_prove‐vdtrytest_fib.sqltest_fib.sql..ok1‐Schemapg_catalogorpublicortapcanok2‐Functionfib(integer)shouldexistok3‐fib(0)shouldbe0notok4‐fib(1)shouldbe1#Failedtest4:"fib(1)shouldbe1"#have:0#want:11..4#Lookslikeyoufailed1testof4Failed1/4subtests

TestSummaryReport‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐test_fib.sql(Wstat:0Tests:4Failed:1)Failedtest:4Files=1,Tests=4,1secs(0.02usr+0.01sys=0.03CPU)Result:FAIL%❚

Thursday, May 21, 2009

Page 28: Unit Test Your Database! (PgCon 2009)

CREATEORREPLACEFUNCTIONfib(fib_forinteger)RETURNSintegerAS$$BEGINRETURNEND;$$LANGUAGEplpgsql;

Modify for the Test

fib_for;0;

Bare minimumThursday, May 21, 2009

Page 29: Unit Test Your Database! (PgCon 2009)

%

Tests Pass!

%psql‐dtry‐ffib.sqlCREATEFUNCTION%pg_prove‐vdtrytest_fib.sqltest_fib.sql..ok1‐Schemapg_catalogorpublicortapcanok2‐Functionfib(integer)shouldexistok3‐fib(0)shouldbe0ok4‐fib(1)shouldbe11..4okAlltestssuccessful.Files=1,Tests=4,0secs(0.02usr+0.01sys=0.03CPU)Result:PASS%❚

Thursday, May 21, 2009

Page 30: Unit Test Your Database! (PgCon 2009)

Add Another Assertion

SELECTcan('{fib}');SELECTcan_ok('fib',ARRAY['integer']);SELECTis(fib(0),0,'fib(0)shouldbe0');SELECTis(fib(1),1,'fib(1)shouldbe1');SELECTis(fib(2),1,'fib(2)shouldbe1');

Thursday, May 21, 2009

Page 31: Unit Test Your Database! (PgCon 2009)

%%psql‐dtry‐ffib.sqlCREATEFUNCTION%pg_prove‐vdtrytest_fib.sqltest_fib.sq1..ok1‐Schemapg_catalogorpublicortapcanok2‐Functionfib(integer)shouldexistok3‐fib(0)shouldbe0ok4‐fib(1)shouldbe1notok5‐fib(2)shouldbe1#Failedtest5:"fib(2)shouldbe1"#have:2#want:11..5#Lookslikeyoufailed1testof5Failed1/5subtests

TestSummaryReport‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐test_fib.sql(Wstat:0Tests:5Failed:1)Failedtest:5Files=1,Tests=5,1secs(0.02usr+0.01sys=0.03CPU)Result:FAIL%❚

Thursday, May 21, 2009

Page 32: Unit Test Your Database! (PgCon 2009)

CREATEORREPLACEFUNCTIONfib(fib_forinteger)RETURNSintegerAS$$BEGIN

Modify to Pass

RETURNfib_for;IFfib_for<2THENRETURNfib_for;ENDIF;RETURNfib_for‐1;

END;$$LANGUAGEplpgsql;

Thursday, May 21, 2009

Page 33: Unit Test Your Database! (PgCon 2009)

%

And…Pass!

%psql‐dtry‐ffib.sqlCREATEFUNCTION%pg_prove‐vdtrytest_fib.sqltest_fib.sql..ok1‐Schemapg_catalogorpublicortapcanok2‐Functionfib(integer)shouldexistok3‐fib(0)shouldbe0ok4‐fib(1)shouldbe1ok5‐fib(2)shouldbe11..5okAlltestssuccessful.Files=1,Tests=5,0secs(0.02usr+0.00sys=0.02CPU)Result:PASS%❚

Thursday, May 21, 2009

Page 34: Unit Test Your Database! (PgCon 2009)

Still More Assertions

SELECTcan('{fib}');SELECTcan_ok('fib',ARRAY['integer']);SELECTis(fib(0),0,'fib(0)shouldbe0');SELECTis(fib(1),1,'fib(1)shouldbe1');SELECTis(fib(2),1,'fib(2)shouldbe1');SELECTis(fib(3),2,'fib(3)shouldbe2');SELECTis(fib(4),3,'fib(4)shouldbe3');SELECTis(fib(5),5,'fib(5)shouldbe5');

Thursday, May 21, 2009

Page 35: Unit Test Your Database! (PgCon 2009)

%%psql‐dtry‐ffib.sqlCREATEFUNCTION%pg_prove‐vdtrytest_fib.sqltest_fib.sq1..1/?notok8‐fib(5)shouldbe5#Failedtest8:"fib(5)shouldbe5"#have:4#want:5#Lookslikeyoufailed1testof8test_fib.sql..Failed1/8subtests

TestSummaryReport‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐test_fib.sql(Wstat:0Tests:8Failed:1)Failedtest:8Files=1,Tests=8,0secs(0.02usr+0.01sys=0.03CPU)Result:FAIL%❚

Thursday, May 21, 2009

Page 36: Unit Test Your Database! (PgCon 2009)

CREATEORREPLACEFUNCTIONfib(fib_forinteger)RETURNSintegerAS$$BEGINIFfib_for<2THENRETURNfib_for;ENDIF;RETURNfib

Fix The Function

(fib_for‐2)_for‐1;+fib(fib_for‐1);END;

$$LANGUAGEplpgsql;

Thursday, May 21, 2009

Page 37: Unit Test Your Database! (PgCon 2009)

%

W00T!

%psql‐dtry‐ffib.sqlCREATEFUNCTION%pg_prove‐vdtrytest_fib.sqltest_fib.sql..okAlltestssuccessful.Files=1,Tests=8,0secs(0.02usr+0.00sys=0.02CPU)Result:PASS%❚

Thursday, May 21, 2009

Page 38: Unit Test Your Database! (PgCon 2009)

A Few More Assertions

SELECTcan('{fib}');SELECTcan_ok('fib',ARRAY['integer']);SELECTis(fib(0),0,'fib(0)shouldbe0');SELECTis(fib(1),1,'fib(1)shouldbe1');SELECTis(fib(2),1,'fib(2)shouldbe1');SELECTis(fib(3),2,'fib(3)shouldbe2');SELECTis(fib(4),3,'fib(4)shouldbe3');SELECTis(fib(5),5,'fib(5)shouldbe5');SELECTis(fib(6),8,'fib(6)shouldbe8');SELECTis(fib(7),13,'fib(7)shouldbe13');SELECTis(fib(8),21,'fib(8)shouldbe21');

Thursday, May 21, 2009

Page 39: Unit Test Your Database! (PgCon 2009)

%

We’re Golden!

%psql‐dtry‐ffib.sqlCREATEFUNCTION%pg_prove‐vdtrytest_fib.sqltest_fib.sql..okAlltestssuccessful.Files=1,Tests=11,0secs(0.02usr+0.01sys=0.03CPU)Result:PASS%❚

Thursday, May 21, 2009

Page 40: Unit Test Your Database! (PgCon 2009)

Make it so, Number One.

Thursday, May 21, 2009

Page 41: Unit Test Your Database! (PgCon 2009)

OMG WTF???

Thursday, May 21, 2009

Page 42: Unit Test Your Database! (PgCon 2009)

The server is hammered!

Thursday, May 21, 2009

Page 43: Unit Test Your Database! (PgCon 2009)

Debug, debug, debug…

Thursday, May 21, 2009

Page 44: Unit Test Your Database! (PgCon 2009)

Nailed it!

Thursday, May 21, 2009

Page 45: Unit Test Your Database! (PgCon 2009)

Detroit, we have a problem.

\timingTimingison.try=#selectfib(30);

try=#

fib‐‐‐‐‐‐‐‐832040(1row)

Time:6752.112mstry=#❚

Thursday, May 21, 2009

Page 46: Unit Test Your Database! (PgCon 2009)

Regression

Thursday, May 21, 2009

Page 47: Unit Test Your Database! (PgCon 2009)

Add a Regression Test

SELECTcan('{fib}');SELECTcan_ok('fib',ARRAY['integer']);SELECTis(fib(0),0,'fib(0)shouldbe0');SELECTis(fib(1),1,'fib(1)shouldbe1');SELECTis(fib(2),1,'fib(2)shouldbe1');SELECTis(fib(3),2,'fib(3)shouldbe2');SELECTis(fib(4),3,'fib(4)shouldbe3');SELECTis(fib(5),5,'fib(5)shouldbe5');SELECTis(fib(6),8,'fib(6)shouldbe8');SELECTis(fib(7),13,'fib(7)shouldbe13');SELECTis(fib(8),21,'fib(8)shouldbe21');SELECTperforms_ok('SELECTfib(30)',500);

Thursday, May 21, 2009

Page 48: Unit Test Your Database! (PgCon 2009)

%

What’ve We Got?

%psql‐dtry‐ffib.sqlCREATEFUNCTION%pg_prove‐vdtrytest_fib.sqltest_fib.sql..12/?notok12‐Shouldruninlessthan500ms#Failedtest12:"Shouldruninlessthan500ms"#runtime:8418.816ms#exceeds:500ms#Lookslikeyoufailed1testof12test_fib.sql..Failed1/12subtests

TestSummaryReport‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐test_fib.sql(Wstat:0Tests:12Failed:1)Failedtest:12Files=1,Tests=12,8secs(0.02usr+0.01sys=0.03CPU)Result:FAIL%❚

Thursday, May 21, 2009

Page 49: Unit Test Your Database! (PgCon 2009)

Refactor

Thursday, May 21, 2009

Page 50: Unit Test Your Database! (PgCon 2009)

CREATEORREPLACEFUNCTIONfib(fib_forinteger)RETURNSintegerAS$$BEGINDECLAREretinteger:=0;nxtinteger:=1;tmpinteger;BEGINFORnumIN0..fib_forLOOPtmp:=ret;ret:=nxt;nxt:=tmp+nxt;ENDLOOP;

RETURNret;END;$$LANGUAGEplpgsql;

Thursday, May 21, 2009

Page 51: Unit Test Your Database! (PgCon 2009)

%%psql‐dtry‐ffib.sqlCREATEFUNCTION%pg_prove‐vdtrytest_fib.sqltest_fib.sq1..1/?notok3‐fib(0)shouldbe0#Failedtest3:"fib(0)shouldbe0"#have:1#want:0notok5‐fib(2)shouldbe1#Failedtest5:"fib(2)shouldbe1"#have:2#want:1notok6‐fib(3)shouldbe2#Failedtest6:"fib(3)shouldbe2"#have:3#want:2notok7‐fib(4)shouldbe3#Failedtest7:"fib(4)shouldbe3"#have:5#want:3notok8‐fib(5)shouldbe5#Failedtest8:"fib(5)shouldbe5"#have:8#want:5notok9‐fib(6)Shouldbe8#Failedtest9:"fib(6)Shouldbe8"#have:13#want:8notok10‐fib(7)Shouldbe13#Failedtest10:"fib(7)Shouldbe13"#have:21#want:13notok11‐fib(8)Shouldbe21#Failedtest11:"fib(8)Shouldbe21"#have:34#want:21#Lookslikeyoufailed8testsof12test_fib.sql..Failed8/12subtests

TestSummaryReport‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐test_fib.sql(Wstat:0Tests:12Failed:8)Failedtests:3,5‐11Files=1,Tests=12,0secs(0.03usr+0.01sys=0.04CPU)Result:FAIL%❚

WTF?

Thursday, May 21, 2009

Page 52: Unit Test Your Database! (PgCon 2009)

CREATEORREPLACEFUNCTIONfib(fib_forinteger)RETURNSintegerAS$$BEGINDECLAREretinteger:=0;nxtinteger:=1;tmpinteger;BEGINFORnumIN..fib_forLOOPtmp:=ret;ret:=nxt;nxt:=tmp+nxt;ENDLOOP;

RETURNret;END;$$LANGUAGEplpgsql;

01

Thursday, May 21, 2009

Page 53: Unit Test Your Database! (PgCon 2009)

%

Back in Business

%psql‐dtry‐ffib.sqlCREATEFUNCTION%pg_prove‐vdtrytest_fib.sqltest_fib.sql..okAlltestssuccessful.Files=1,Tests=12,1secs(0.02usr+0.01sys=0.03CPU)Result:PASS%❚

Thursday, May 21, 2009

Page 54: Unit Test Your Database! (PgCon 2009)

⌀Thursday, May 21, 2009

Page 55: Unit Test Your Database! (PgCon 2009)

Just for the Hell of it…

Thursday, May 21, 2009

Page 56: Unit Test Your Database! (PgCon 2009)

Push It!

SELECTcan('{fib}');SELECTcan_ok('fib',ARRAY['integer']);SELECTis(fib(0),0,'fib(0)shouldbe0');SELECTis(fib(1),1,'fib(1)shouldbe1');SELECTis(fib(2),1,'fib(2)shouldbe1');SELECTis(fib(3),2,'fib(3)shouldbe2');SELECTis(fib(4),3,'fib(4)shouldbe3');SELECTis(fib(5),5,'fib(5)shouldbe5');SELECTis(fib(6),8,'fib(6)shouldbe8');SELECTis(fib(7),13,'fib(7)shouldbe13');SELECTis(fib(8),21,'fib(8)shouldbe21');SELECTperforms_ok('SELECTfib(30)',500);SELECTis(fib(32),2178309,'fib(32)is2178309');SELECTis(fib(64),10610209857723,'fib(64)is10610209857723');

Thursday, May 21, 2009

Page 57: Unit Test Your Database! (PgCon 2009)

%

No Fibbing.

%psql‐dtry‐ffib.sqlCREATEFUNCTION%pg_prove‐vdtrytest_fib.sqltest_fib.sql..1/?psql:test_fib.sql:18:ERROR:functionis(integer,bigint,unknown)doesnotexistLINE1:SELECTis(fib(64),10610209857723,'fib(64)Shouldbe10610...^HINT:Nofunctionmatchesthegivennameandargumenttypes.Youmightneedtoaddexplicittypecasts.test_fib.sql..Dubious,testreturned3(wstat768,0x300)All13subtestspassed

TestSummaryReport‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐test_fib.sql(Wstat:768Tests:13Failed:0)Non‐zeroexitstatus:3Parseerrors:NoplanfoundinTAPoutputFiles=1,Tests=13,0secs(0.02usr+0.01sys=0.03CPU)Result:FAIL

%❚

Hrm…

Thursday, May 21, 2009

Page 58: Unit Test Your Database! (PgCon 2009)

CREATEORREPLACEFUNCTIONfib(fib_forinteger)RETURNSAS$$BEGINDECLAREret:=0;nxt:=1;tmp;BEGINFORnumIN1..fib_forLOOPtmp:=ret;ret:=nxt;nxt:=tmp+nxt;ENDLOOP;

RETURNret;END;$$LANGUAGEplpgsql;

bigintinteger

integerintegerinteger

bigintbigintbigint

Thursday, May 21, 2009

Page 59: Unit Test Your Database! (PgCon 2009)

SELECTcan('{fib}');SELECTcan_ok('fib',ARRAY['integer']);SELECTis(fib(0),0,'fib(0)shouldbe0');SELECTis(fib(1),1,'fib(1)shouldbe1');SELECTis(fib(2),1,'fib(2)shouldbe1');SELECTis(fib(3),2,'fib(3)shouldbe2');SELECTis(fib(4),3,'fib(4)shouldbe3');SELECTis(fib(5),5,'fib(5)shouldbe5');SELECTis(fib(6),8,'fib(6)shouldbe8');SELECTis(fib(7),13,'fib(7)shouldbe13');SELECTis(fib(8),21,'fib(8)shouldbe21');SELECTperforms_ok('SELECTfib(30)',500);SELECTis(fib(32),2178309,'fib(32)is2178309');SELECTis(fib(64),10610209857723,'fib(64)is10610209857723');

SELECTcan('{fib}');SELECTcan_ok('fib',ARRAY['integer']);SELECTis(fib(0),0::int8,'fib(0)shouldbe0');SELECTis(fib(1),1::int8,'fib(1)shouldbe1');SELECTis(fib(2),1::int8,'fib(2)shouldbe1');SELECTis(fib(3),2::int8,'fib(3)shouldbe2');SELECTis(fib(4),3::int8,'fib(4)shouldbe3');SELECTis(fib(5),5::int8,'fib(5)shouldbe5');SELECTis(fib(6),8::int8,'fib(6)shouldbe8');SELECTis(fib(7),13::int8,'fib(7)shouldbe13');SELECTis(fib(8),21::int8,'fib(8)shouldbe21');SELECTperforms_ok('SELECTfib(30)',500);SELECTis(fib(32),2178309::int8,'fib(32)is2178309');SELECTis(fib(64),10610209857723::int8,'fib(64)is10610209857723');

Apples to Apples…

Thursday, May 21, 2009

Page 60: Unit Test Your Database! (PgCon 2009)

%

And Now?

%psql‐dtry‐ffib.sqlCREATEFUNCTION%pg_prove‐vdtrytest_fib.sqltest_fib.sql..okAlltestssuccessful.Files=1,Tests=14,0secs(0.02usr+0.01sys=0.03CPU)Result:PASS%❚

Thursday, May 21, 2009

Page 61: Unit Test Your Database! (PgCon 2009)

TDD Means Consistency

Thursday, May 21, 2009

Page 62: Unit Test Your Database! (PgCon 2009)

What about Maintenance?

Thursday, May 21, 2009

Page 63: Unit Test Your Database! (PgCon 2009)

CREATEFUNCTIONfind_by_birthday(integer,integer,integer,integer,text)RETURNSSETOFintegerAS$$DECLAREp_dayALIASFOR$1;p_monALIASFOR$2;p_offsetALIASFOR$3;p_limitALIASFOR$4;p_stateALIASFOR$5;v_qryTEXT;v_outputRECORD;BEGINv_qry:='SELECT*FROMusersWHEREstate='''||p_state||'''';v_qry:=v_qry||'ANDbirth_mon~''^0?'||p_mon::text||'$''';v_qry:=v_qry||'ANDbirth_day='''||p_day::text||'''';v_qry:=v_qry||'ORDERBYuser_id';IFp_offsetISNOTNULLTHENv_qry:=v_qry||'OFFSET'||p_offset;ENDIF;IFp_limitISNOTNULLTHENv_qry:=v_qry||'LIMIT'||p_limit;ENDIF;FORv_outputINEXECUTEv_qryLOOPRETURNNEXTv_output.user_id;ENDLOOP;RETURN;END;$$LANGUAGEplpgsql;

Thursday, May 21, 2009

Page 64: Unit Test Your Database! (PgCon 2009)

What’s on the Table?

\dusersTable"public.users"Column|Type|Modifiers‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐user_id|integer|notnulldefaultnextval(…)name|text|notnulldefault''::textbirthdate|date|birth_mon|charactervarying(2)|birth_day|charactervarying(2)|birth_year|charactervarying(4)|state|text|notnulldefault'active'::textIndexes:"users_pkey"PRIMARYKEY,btree(user_id)

try=#

Thursday, May 21, 2009

Page 65: Unit Test Your Database! (PgCon 2009)

What’s on the Table?

select*fromusers;user_id|name|birthdate|birth_mon|birth_day|birth_year|state‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐1|David|1968‐12‐19|12|19|1968|active2|Josh|1970‐03‐12|03|12|1970|active3|Dan|1972‐06‐03|6|3|1972|active(3rows)

try=#

Thursday, May 21, 2009

Page 66: Unit Test Your Database! (PgCon 2009)

Must…restrain…fist…of death…

Thursday, May 21, 2009

Page 67: Unit Test Your Database! (PgCon 2009)

The Situation

This is production code

Cannot afford downtime

No room for mistakes

Bugs must remain consistent

But…

Thursday, May 21, 2009

Page 68: Unit Test Your Database! (PgCon 2009)

Dear GOD it needs rewriting.

Thursday, May 21, 2009

Page 69: Unit Test Your Database! (PgCon 2009)

But first…

Thursday, May 21, 2009

Page 70: Unit Test Your Database! (PgCon 2009)

Test the existing implementation.

Thursday, May 21, 2009

Page 71: Unit Test Your Database! (PgCon 2009)

BEGIN;SETsearch_pathTOpublic,tap;SELECTplan(13);

SELECThas_table('users');SELECThas_pk('users');

SELECThas_column('users','user_id');SELECTcol_type_is('users','user_id','integer');SELECTcol_is_pk('users','user_id');SELECTcol_not_null('users','user_id');

SELECThas_column('users','birthdate');SELECTcol_type_is('users','birthdate','date');SELECTcol_is_null('users','birthdate');

SELECThas_column('users','state');SELECTcol_type_is('users','state','text');SELECTcol_not_null('users','state');SELECTcol_default_is('users','state','active');

SELECT*FROMfinish();ROLLBACK;

Thursday, May 21, 2009

Page 72: Unit Test Your Database! (PgCon 2009)

%

Schema Sanity

%pg_prove‐dtrytest_schema.sqltest_schema.sql..okAlltestssuccessful.Files=1,Tests=13,0secs(0.02usr+0.01sys=0.03CPU)Result:PASS%❚

Thursday, May 21, 2009

Page 73: Unit Test Your Database! (PgCon 2009)

BEGIN;SETsearch_pathTOpublic,tap;‐‐SELECTplan(15);SELECT*FROMno_plan();

SELECTcan('{find_by_birthday}');SELECTcan_ok('find_by_birthday',ARRAY['integer','integer','integer','integer','text']);

‐‐Setupfixtures.ALTERSEQUENCEusers_user_id_seqRESTART1;INSERTINTOusers(name,birthdate,birth_mon,birth_day,birth_year)VALUES('David','1968‐12‐19','12','19','1968'),('Josh','1970‐03‐12','03','12','1970'),('Dan','1972‐06‐03','6','3','1972'),('Anna','2005‐06‐03','06','3','2005');

SELECTis(ARRAY(SELECT*FROMfind_by_birthday(19,12,NULL,NULL,'active')),ARRAY[1],'Shouldfetchonebirthdayfor12/19');

SELECT*FROMfinish();ROLLBACK;

Thursday, May 21, 2009

Page 74: Unit Test Your Database! (PgCon 2009)

%

How We Doin’?

%pg_prove‐dtrytest_schema.sqltest_find_by_bday.sql..okAlltestssuccessful.Files=1,Tests=3,0secs(0.02usr+0.01sys=0.03CPU)Result:PASS%❚

Thursday, May 21, 2009

Page 75: Unit Test Your Database! (PgCon 2009)

SELECTis(ARRAY(SELECT*FROMfind_by_birthday(19,12,NULL,NULL,'active')),ARRAY[1],'Shouldfetchonebirthdayfor12/19');SELECTis(ARRAY(SELECT*FROMfind_by_birthday(3,6,NULL,NULL,'active')),ARRAY[3,4],'Shouldfetchtwobirthdaysfor3/6');SELECTis(ARRAY(SELECT*FROMfind_by_birthday(3,6,1,NULL,'active')),ARRAY[4],'Shouldfetchonebirthdayfor3/6OFFSET1');SELECTis(ARRAY(SELECT*FROMfind_by_birthday(3,6,NULL,1,'active')),ARRAY[3],'Shouldfetchonebirthdayfor3/6LIMIT1');UPDATEusersSETstate='inactive'WHEREuser_id=3;SELECTis(ARRAY(SELECT*FROMfind_by_birthday(3,6NULL,NULL,'active')),ARRAY[4],'Shouldfetchoneactivebirthdayfor3/6');SELECTis(ARRAY(SELECT*FROMfind_by_birthday(3,6,NULL,NULL,'inactive')),ARRAY[3],'Shouldfetchoneinactivebirthdayfor3/6');

Thursday, May 21, 2009

Page 76: Unit Test Your Database! (PgCon 2009)

%

Still Good…

%pg_prove‐dtrytest_schema.sqltest_find_by_bday.sql..okAlltestssuccessful.Files=1,Tests=8,0secs(0.02usr+0.01sys=0.03CPU)Result:PASS%❚

Thursday, May 21, 2009

Page 77: Unit Test Your Database! (PgCon 2009)

NOW We Can Refactor

Thursday, May 21, 2009

Page 78: Unit Test Your Database! (PgCon 2009)

Let’s Go with SQL

CREATEORREPLACEFUNCTIONfind_by_birthday(p_dayinteger,p_moninteger,p_offsetinteger,p_limitinteger,p_statetext)RETURNSSETOFintegerAS$$SELECTuser_idFROMusersWHEREstate=COALESCE($5,'active')ANDEXTRACT(dayFROMbirthdate)=$1ANDEXTRACT(monthFROMbirthdate)=$2ORDERBYuser_idOFFSETCOALESCE($3,NULL)LIMITCOALESCE($4,NULL)$$LANGUAGEsql;

Thursday, May 21, 2009

Page 79: Unit Test Your Database! (PgCon 2009)

%

And That’s That

%pg_prove‐dtrytest_schema.sqltest_find_by_bday.sql..okAlltestssuccessful.Files=1,Tests=8,0secs(0.02usr+0.01sys=0.03CPU)Result:PASS%❚

Thursday, May 21, 2009

Page 80: Unit Test Your Database! (PgCon 2009)

Hell Yes!

Thursday, May 21, 2009

Page 81: Unit Test Your Database! (PgCon 2009)

Let’s Review

Thursday, May 21, 2009

Page 82: Unit Test Your Database! (PgCon 2009)

Tests are for Finding Bugs

TDD not for finding bugs

TDD for sanity and consistency

Tests prevent future bugs

Thursday, May 21, 2009

Page 83: Unit Test Your Database! (PgCon 2009)

Tests are Hard

Good frameworks easy

pgTAP provides lots of assertions

If you mean Hard to test interface:

Red flag

Think about refactoring

If it’s hard to test…

It’s hard to use

Thursday, May 21, 2009

Page 84: Unit Test Your Database! (PgCon 2009)

Never Find Relevant Bugs

Tests don’t find bugs

Test PREVENT bugs

If your code doesn’t work…

That failure is RELEVANT, no?

Thursday, May 21, 2009

Page 85: Unit Test Your Database! (PgCon 2009)

Time-Consuming

Good frameworks easy to use

Iterating between tests and code is natural

Tests are as fast as your code

Not as time-consuming as bug hunting

When no tests, bugs repeat themselves

And are harder to track down

Talk about a time sink!

Thursday, May 21, 2009

Page 86: Unit Test Your Database! (PgCon 2009)

Running Tests is Slow

Test what you’re working on

Set up automated testing for everything else

Pay attention to automated test failures

Thursday, May 21, 2009

Page 87: Unit Test Your Database! (PgCon 2009)

For Inexperienced Developers

I’ve been programming for 10 years

I have no idea what I was thinking a year ago

Tests make maintenance a breeze

They give me the confidence to make changes without fearing the consequences

Tests represent FREEDOM from the tyranny of fragility and inconsistency

Thursday, May 21, 2009

Page 88: Unit Test Your Database! (PgCon 2009)

Unnecessary for Simple Code

I copied fib() from a Perl library

It was dead simple

And it was still wrong

Tests keep even the simplest code working

Thursday, May 21, 2009

Page 89: Unit Test Your Database! (PgCon 2009)

Best for Fragile Code

All code is fragile

Tests make code ROBUSTAdd regression tests for bugs found by

Integration tests

QA department

Your users

Thursday, May 21, 2009

Page 90: Unit Test Your Database! (PgCon 2009)

Users Test our Code

Talk about fragility

Staging servers never work

QA departments are disappearing

Users don’t want to see bugs

Find ways to test your code

Users avoid fragile applications

Thursday, May 21, 2009

Page 91: Unit Test Your Database! (PgCon 2009)

It’s a Private Function

It still needs to work

It still needs to always work

Don’t reject glass box testing

Make sure that ALL interfaces work

Thursday, May 21, 2009

Page 92: Unit Test Your Database! (PgCon 2009)

Application Tests are Sufficient

App tests should connect as as app user

May well be security limitations for the app

Access only to functions

Apps cannot adequately test the database

Database tests should test the database

Application tests should test the application

Thursday, May 21, 2009

Page 93: Unit Test Your Database! (PgCon 2009)

Tests Prove Nothing

This is not a math equation

This is about:

consistency

stability

robusticity

If a test fails, it has proved a failure

Think Karl Popper

Thursday, May 21, 2009

Page 94: Unit Test Your Database! (PgCon 2009)

Tests are for Stable Code

How does it become stable?

Tests the fastest route

Ensure greater stability over time

TDD help with working through issues

TDD helps thinking through interfaces

Tests encourage experimentation

Thursday, May 21, 2009

Page 95: Unit Test Your Database! (PgCon 2009)

I Really Like Detroit

I can’t help you

Thursday, May 21, 2009

Page 96: Unit Test Your Database! (PgCon 2009)

What’re You Waiting For?

pgTAP: http://pgtap.projects.postgresql.org

pgUnit: http://en.dklab.ru/lib/dklab_pgunit

EpicTest: http://www.epictest.org

pg_regress

Thursday, May 21, 2009

Page 97: Unit Test Your Database! (PgCon 2009)

Start writing tests

Thursday, May 21, 2009

Page 98: Unit Test Your Database! (PgCon 2009)

Increase consistency

Thursday, May 21, 2009

Page 99: Unit Test Your Database! (PgCon 2009)

Improve stability

Thursday, May 21, 2009

Page 100: Unit Test Your Database! (PgCon 2009)

Save time

Thursday, May 21, 2009

Page 101: Unit Test Your Database! (PgCon 2009)

Free yourself

Thursday, May 21, 2009

Page 102: Unit Test Your Database! (PgCon 2009)

and…

Thursday, May 21, 2009

Page 103: Unit Test Your Database! (PgCon 2009)

Kick ass.Thursday, May 21, 2009

Page 104: Unit Test Your Database! (PgCon 2009)

Unit Test Your Database!

David E. WheelerPostgreSQL Experts, Inc.

PGCon, May 21, 2009

Thank You

Thursday, May 21, 2009