Unit Test Your Database! (PgCon 2009)
-
Upload
postgresql-experts-inc -
Category
Technology
-
view
397 -
download
4
description
Transcript of Unit Test Your Database! (PgCon 2009)
![Page 1: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/1.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/2.jpg)
Why?Thursday, May 21, 2009
![Page 3: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/3.jpg)
Do thesesound familiar?
Thursday, May 21, 2009
![Page 4: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/4.jpg)
“It takes too long to write tests.”
Thursday, May 21, 2009
![Page 5: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/5.jpg)
“Testing will just slow me down.”
Thursday, May 21, 2009
![Page 6: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/6.jpg)
“It takes too long to run tests.”
Thursday, May 21, 2009
![Page 7: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/7.jpg)
“We already write app-level unit tests.”
Thursday, May 21, 2009
![Page 8: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/8.jpg)
“I test stuff by running my app.”
Thursday, May 21, 2009
![Page 9: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/9.jpg)
“Tests never find relevant bugs.”
Thursday, May 21, 2009
![Page 10: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/10.jpg)
“This code is so simple it doesn’t need tests.”
Thursday, May 21, 2009
![Page 11: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/11.jpg)
“This function is too hard to test.”
Thursday, May 21, 2009
![Page 12: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/12.jpg)
“This is a private function.”
Thursday, May 21, 2009
![Page 13: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/13.jpg)
“Tests can't prove a program correct so why bother?”
Thursday, May 21, 2009
![Page 14: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/14.jpg)
“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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/15.jpg)
“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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/16.jpg)
“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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/17.jpg)
“I’m lucky enough to only be dealing with really good developers.”
Thursday, May 21, 2009
![Page 18: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/18.jpg)
“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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/19.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/20.jpg)
Let’s Get Real
Thursday, May 21, 2009
![Page 21: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/21.jpg)
What does it take?
Thursday, May 21, 2009
![Page 22: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/22.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/23.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/24.jpg)
CREATEORREPLACEFUNCTIONfib(fib_forinteger)RETURNSintegerAS$$BEGINRETURN0;END;$$LANGUAGEplpgsql;
Simple Function
Thursday, May 21, 2009
![Page 25: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/25.jpg)
%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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/26.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/27.jpg)
%%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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/28.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/29.jpg)
%
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/30.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/31.jpg)
%%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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/32.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/33.jpg)
%
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/34.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/35.jpg)
%%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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/36.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/37.jpg)
%
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/38.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/39.jpg)
%
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/40.jpg)
Make it so, Number One.
Thursday, May 21, 2009
![Page 41: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/41.jpg)
OMG WTF???
Thursday, May 21, 2009
![Page 42: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/42.jpg)
The server is hammered!
Thursday, May 21, 2009
![Page 43: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/43.jpg)
Debug, debug, debug…
Thursday, May 21, 2009
![Page 44: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/44.jpg)
Nailed it!
Thursday, May 21, 2009
![Page 45: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/45.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/46.jpg)
Regression
Thursday, May 21, 2009
![Page 47: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/47.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/48.jpg)
%
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/49.jpg)
Refactor
Thursday, May 21, 2009
![Page 50: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/50.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/51.jpg)
%%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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/52.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/53.jpg)
%
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/54.jpg)
⌀Thursday, May 21, 2009
![Page 55: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/55.jpg)
Just for the Hell of it…
Thursday, May 21, 2009
![Page 56: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/56.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/57.jpg)
%
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/58.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/59.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/60.jpg)
%
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/61.jpg)
TDD Means Consistency
Thursday, May 21, 2009
![Page 62: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/62.jpg)
What about Maintenance?
Thursday, May 21, 2009
![Page 63: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/63.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/64.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/65.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/66.jpg)
Must…restrain…fist…of death…
Thursday, May 21, 2009
![Page 67: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/67.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/68.jpg)
Dear GOD it needs rewriting.
Thursday, May 21, 2009
![Page 69: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/69.jpg)
But first…
Thursday, May 21, 2009
![Page 70: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/70.jpg)
Test the existing implementation.
Thursday, May 21, 2009
![Page 71: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/71.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/72.jpg)
%
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/73.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/74.jpg)
%
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/75.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/76.jpg)
%
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/77.jpg)
NOW We Can Refactor
Thursday, May 21, 2009
![Page 78: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/78.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/79.jpg)
%
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/80.jpg)
Hell Yes!
Thursday, May 21, 2009
![Page 81: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/81.jpg)
Let’s Review
Thursday, May 21, 2009
![Page 82: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/82.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/83.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/84.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/85.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/86.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/87.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/88.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/89.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/90.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/91.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/92.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/93.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/94.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/95.jpg)
I Really Like Detroit
I can’t help you
Thursday, May 21, 2009
![Page 96: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/96.jpg)
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)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/97.jpg)
Start writing tests
Thursday, May 21, 2009
![Page 98: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/98.jpg)
Increase consistency
Thursday, May 21, 2009
![Page 99: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/99.jpg)
Improve stability
Thursday, May 21, 2009
![Page 100: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/100.jpg)
Save time
Thursday, May 21, 2009
![Page 101: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/101.jpg)
Free yourself
Thursday, May 21, 2009
![Page 102: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/102.jpg)
and…
Thursday, May 21, 2009
![Page 103: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/103.jpg)
Kick ass.Thursday, May 21, 2009
![Page 104: Unit Test Your Database! (PgCon 2009)](https://reader038.fdocuments.us/reader038/viewer/2022102922/54660d49af7959f12b8b5895/html5/thumbnails/104.jpg)
Unit Test Your Database!
David E. WheelerPostgreSQL Experts, Inc.
PGCon, May 21, 2009
Thank You
Thursday, May 21, 2009