2. Who I am
Delphi developer since 1999
IT Consultant
Front end web developer
Postgresql addicted
Nonantolando.blogspot.com
lucio.grenzi
lucio grenzi
PGDay.IT 2013 – 25 Ottobre 2013 - Prato
2 di 24
4. Question before starting
Why would you want to unit
test your database?
PGDay.IT 2013 – 25 Ottobre 2013 - Prato
4 di 24
5. Why use pgTap
Backend application development
Test schema object validation
Module development
Continuos integration
PGDay.IT 2013 – 25 Ottobre 2013 - Prato
5 di 24
6. Tap protocol
The Test Anything Protocol (TAP) is a protocol to allow
communication between unit tests and a test harness. It
allows individual tests (TAP producers) to communicate
test results to the testing harness in a language-agnostic
way. Originally developed for unit testing of the Perl
interpreter in 1987, producers and parsers are now
available for many development platforms.
-wikipedia-
PGDay.IT 2013 – 25 Ottobre 2013 - Prato
6 di 24
7. PgTap
pgTAP is a unit testing framework for PostgreSQL written
in PL/pgSQL and PL/SQL. It includes a comprehensive
collection of TAP-emitting assertion functions, as well as
the ability to integrate with other TAP-emitting test
frameworks. It can also be used in the xUnit testing style.
-http://pgtap.org/-
PGDay.IT 2013 – 25 Ottobre 2013 - Prato
7 di 24
8. PgTap now
www.pgtap.org
Latest version is 0.93.0
Already packaged for the most important linux
distributions
make
make installcheck
make install
PGDay.IT 2013 – 25 Ottobre 2013 - Prato
8 di 24
9. Requirements
PostgreSQL 8.1 or higher
with 8.4 or higher recommended for full use of its API
PL/pgSQL
On Windows servers is necessary to install Perl
Perl on Linux is no more necessary but it is required by
pg_prove
PGDay.IT 2013 – 25 Ottobre 2013 - Prato
9 di 24
10. Adding PgTap to a database
Install pgtap in a database
psql -d dbname -f pgtap.sql
include the call to pgtap.sql in your script with
/pgtap.sql
i
remove pgtap from a database
psql -d dbname -f uninstall_pgtap.sql
PGDay.IT 2013 – 25 Ottobre 2013 - Prato
10 di 24
11. Tap in practice
Test output is easy to understand
BEGIN;
SELECT plan(); ---- how many test?
…put your tests here…
SELECT * FROM finish(); ---- test finished, print report
ROLLBACK;
PGDay.IT 2013 – 25 Ottobre 2013 - Prato
11 di 24
15. PgTap basics
set ON_ERROR_ROLLBACK 11
set ON_ERROR_ROLLBACK
set ON_ERROR_STOP true
set ON_ERROR_STOP true
set QUIET 11
set QUIET
BEGIN;
BEGIN;
SELECT plan(1);
SELECT plan(1);
SELECT pass( 'Hello PgDayit !'!');
SELECT pass( 'Hello PgDayit );
SELECT **FROM finish();
SELECT FROM finish();
ROLLBACK;
ROLLBACK;
save this as HelloPgDayit.txt and type: psql -U postgres -f HelloPgDayit.txt
1..1
1..1
ok 11- -Hello PgDayit ! !
ok
Hello PgDayit
PGDay.IT 2013 – 25 Ottobre 2013 - Prato
15 di 24
16. Let's create some tables
BEGIN;
i ./pgtap.sql
-- create two tables with referential constraint
create table table1 (id integer not null, t_text varchar(100), dt timestamp default now(), CONSTRAINT table1_pkey
PRIMARY KEY (id));
create table table2 (id integer not null, t_text varchar(100), id_ref integer, CONSTRAINT id_ref FOREIGN KEY
(id_ref) REFERENCES table1 (id));
insert into table1 (id,t_text) values (1,'test one');
insert into table1 (id,t_text) values (2,'test two');
insert into table1 (id,t_text) values (3,'test three');
insert into table2 (id,t_text,id_ref) values (1,'ref test one', 1);
insert into table2 (id,t_text,id_ref) values (2,'ref test two', 2);
insert into table2 (id,t_text,id_ref) values (3,'ref test three', 3);
SELECT plan(6);
## type tests here##
## get results here##
SELECT * FROM finish();
ROLLBACK;
PGDay.IT 2013 – 25 Ottobre 2013 - Prato
16 di 24
17. Test samples
PREPARE ids_fetched AS
PREPARE ids_fetched AS
select id from table1 where id in (1,2,3) order by id asc;
select id from table1 where id in (1,2,3) order by id asc;
PREPARE ids_expected AS VALUES (1),(2),(3);
PREPARE ids_expected AS VALUES (1),(2),(3);
SELECT results_eq( 'ids_fetched', 'ids_expected',
SELECT results_eq( 'ids_fetched', 'ids_expected',
'fetched the expected ids from table1');
'fetched the expected ids from table1');
PREPARE ids_fetched1 AS select id
PREPARE ids_fetched1 AS select id
from table1 where id in (1,2,3) order by id asc;
from table1 where id in (1,2,3) order by id asc;
PREPARE ids_fetched2 AS select id
PREPARE ids_fetched2 AS select id
from table2 where id in (1,2,3) order by id asc;
from table2 where id in (1,2,3) order by id asc;
SELECT results_eq( 'ids_fetched1', 'ids_fetched2');
SELECT results_eq( 'ids_fetched1', 'ids_fetched2');
PREPARE throw_error AS
PREPARE throw_error AS
insert into table1 (id,t_text)
insert into table1 (id,t_text)
values (1,'duplicate key error');
values (1,'duplicate key error');
SELECT throws_ok('throw_error','23505',NULL,
SELECT throws_ok('throw_error','23505',NULL,
'duplicated key found (id)');
'duplicated key found (id)');
PGDay.IT 2013 – 25 Ottobre 2013 - Prato
17 di 24
18. pg_prove
command-line application to run one or more pgTAP
tests in a PostgreSQL database
output of the tests is processed by TAP::Harness in
order to summarize the results
Tests can be written as:
SQL scripts
xUnit-style database functions
PGDay.IT 2013 – 25 Ottobre 2013 - Prato
18 di 24
20. pg_prove - xUnit Test Functions
EATE OR REPLACE FUNCTION setup_insert(
REATE OR REPLACE FUNCTION setup_insert(
RETURNS SETOF TEXT AS $$
RETURNS SETOF TEXT AS $$
RETURN NEXT is( MAX(lucio), NULL, 'Should have no users') FROM speakers;
RETURN NEXT is( MAX(lucio), NULL, 'Should have no users') FROM speakers;
INSERT INTO speakers (lucio) VALUES ('theory');
INSERT INTO speakers (lucio) VALUES ('theory');
LANGUAGE plpgsql;
$ LANGUAGE plpgsql;
eate OR REPLACE FUNCTION test_user(
reate OR REPLACE FUNCTION test_user(
RETURNS SETOF TEXT AS $$
RETURNS SETOF TEXT AS $$
SELECT is( lucio, 'theory', 'Should have nick') FROM speakers;
SELECT is( lucio, 'theory', 'Should have nick') FROM speakers;
D;
ND;
LANGUAGE sql;
$ LANGUAGE sql;
% pg_prove dbname pgdayit runtests
% pg_prove dbname pgdayit runtests
runtests()....ok
runtests()....ok
All tests successful.
All tests successful.
Files=1, Tests=16, 0 wallclock secs
Files=1, Tests=16, 0 wallclock secs
( 0.02 usr 0.01 sys + 0.01 cusr 0.00 csys = 0.04 CPU)
( 0.02 usr 0.01 sys + 0.01 cusr 0.00 csys = 0.04 CPU)
Result: PASS
Result: PASS
PGDay.IT 2013 – 25 Ottobre 2013 - Prato
20 di 24
21. Conclusions
There are functions for almost everything in your
postgresql db
Triggers, Functions, Schemas, Tablespaces, ….
It is possible create relationships of, or better conditional,
tests
Stable
PGDay.IT 2013 – 25 Ottobre 2013 - Prato
21 di 24
22. Risorse
Citare tutte le risorse utili:
www.pgtap.org
https://github.com/theory/pgtap
PGDay.IT 2013 – 25 Ottobre 2013 - Prato
22 di 24