unit test your database! (pgcon 2009)

Post on 14-Nov-2014

398 Views

Category:

Technology

4 Downloads

Preview:

Click to see full reader

DESCRIPTION

 

TRANSCRIPT

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

top related