Upload
postgresql-experts-inc
View
398
Download
4
Tags:
Embed Size (px)
DESCRIPTION
Citation preview
Unit Test Your Database!
David E. WheelerPostgreSQL Experts, Inc.
PGCon, May 21, 2009
Thursday, May 21, 2009
Why?Thursday, May 21, 2009
Do thesesound familiar?
Thursday, May 21, 2009
“It takes too long to write tests.”
Thursday, May 21, 2009
“Testing will just slow me down.”
Thursday, May 21, 2009
“It takes too long to run tests.”
Thursday, May 21, 2009
“We already write app-level unit tests.”
Thursday, May 21, 2009
“I test stuff by running my app.”
Thursday, May 21, 2009
“Tests never find relevant bugs.”
Thursday, May 21, 2009
“This code is so simple it doesn’t need tests.”
Thursday, May 21, 2009
“This function is too hard to test.”
Thursday, May 21, 2009
“This is a private function.”
Thursday, May 21, 2009
“Tests can't prove a program correct so why bother?”
Thursday, May 21, 2009
“The behavior of the code changes a lot and rewriting the tests to match will just slow things down.”
Thursday, May 21, 2009
“If I imagined a problem to write tests for, I probably wrote code that doesn’t have that problem.”
Thursday, May 21, 2009
“I can produce software that works even without focusing specifically on low- level unit tests.”
Thursday, May 21, 2009
“I’m lucky enough to only be dealing with really good developers.”
Thursday, May 21, 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
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
Let’s Get Real
Thursday, May 21, 2009
What does it take?
Thursday, May 21, 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
Simple Test
BEGIN;SETsearch_pathTOpublic,tap;SELECT*FROMno_plan();
SELECTcan('{fib}');SELECTcan_ok('fib',ARRAY['integer']);
SELECT*FROMfinish();ROLLBACK;
Thursday, May 21, 2009
CREATEORREPLACEFUNCTIONfib(fib_forinteger)RETURNSintegerAS$$BEGINRETURN0;END;$$LANGUAGEplpgsql;
Simple Function
Thursday, May 21, 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
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
%%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
CREATEORREPLACEFUNCTIONfib(fib_forinteger)RETURNSintegerAS$$BEGINRETURNEND;$$LANGUAGEplpgsql;
Modify for the Test
fib_for;0;
Bare minimumThursday, May 21, 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
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
%%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
CREATEORREPLACEFUNCTIONfib(fib_forinteger)RETURNSintegerAS$$BEGIN
Modify to Pass
RETURNfib_for;IFfib_for<2THENRETURNfib_for;ENDIF;RETURNfib_for‐1;
END;$$LANGUAGEplpgsql;
Thursday, May 21, 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
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
%%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
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
%
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
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
%
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
Make it so, Number One.
Thursday, May 21, 2009
OMG WTF???
Thursday, May 21, 2009
The server is hammered!
Thursday, May 21, 2009
Debug, debug, debug…
Thursday, May 21, 2009
Nailed it!
Thursday, May 21, 2009
Detroit, we have a problem.
\timingTimingison.try=#selectfib(30);
try=#
fib‐‐‐‐‐‐‐‐832040(1row)
Time:6752.112mstry=#❚
❚
Thursday, May 21, 2009
Regression
Thursday, May 21, 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
%
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
Refactor
Thursday, May 21, 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
%%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
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
%
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
⌀Thursday, May 21, 2009
Just for the Hell of it…
Thursday, May 21, 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
%
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
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
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
%
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
TDD Means Consistency
Thursday, May 21, 2009
What about Maintenance?
Thursday, May 21, 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
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
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
Must…restrain…fist…of death…
Thursday, May 21, 2009
The Situation
This is production code
Cannot afford downtime
No room for mistakes
Bugs must remain consistent
But…
Thursday, May 21, 2009
Dear GOD it needs rewriting.
Thursday, May 21, 2009
But first…
Thursday, May 21, 2009
Test the existing implementation.
Thursday, May 21, 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
%
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
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
%
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
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
%
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
NOW We Can Refactor
Thursday, May 21, 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
%
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
Hell Yes!
Thursday, May 21, 2009
Let’s Review
Thursday, May 21, 2009
Tests are for Finding Bugs
TDD not for finding bugs
TDD for sanity and consistency
Tests prevent future bugs
Thursday, May 21, 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
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
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
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
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
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
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
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
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
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
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
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
I Really Like Detroit
I can’t help you
Thursday, May 21, 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
Start writing tests
Thursday, May 21, 2009
Increase consistency
Thursday, May 21, 2009
Improve stability
Thursday, May 21, 2009
Save time
Thursday, May 21, 2009
Free yourself
Thursday, May 21, 2009
and…
Thursday, May 21, 2009
Kick ass.Thursday, May 21, 2009
Unit Test Your Database!
David E. WheelerPostgreSQL Experts, Inc.
PGCon, May 21, 2009
Thank You
Thursday, May 21, 2009