Qualche giorno fa parlavo di pgtap e accennavo alla possibilità di usarlo per usare il TDD direttamente con il database, sfruttando il fatto che PostgreSQL consente l’uso di transazioni anche per
i comandi di definizione delete database.
Questo è un grosso vantaggio perché posso definire nuovi oggetti dello schema senza che eventuali errori si propaghino al di fuori della sessione corrente.
NB: L’esempio che segue è prolisso e probabilmente simile a innumerevoli altri esempi sulle virtù del “test infected” ma mi sembra comunque degno di nota che si possa usare questo approccio anche all’interno di un database. Bene, cominciamo:
\set ECHO
\set QUIET 1
\pset format unaligned
\pset tuples_only true
\pset pager
\set ON_ERROR_ROLLBACK 1
\set ON_ERROR_STOP true
\set QUIET 1
Dopo aver impostato alcuni parametri per psql, proviamo a definire un nuovo domain che verrà poi utilizzato da una nuova tabella.
Ho installato pgtap in uno schema a sé stante, in modo che le sue funzioni non si mischino con quelle dello schema “vero”.
Visto che vogliamo sviluppare in modalità TDD, creiamo subito il test per verificare la presenza del domain, che ovviamente fallirà.
BEGIN;
SET search_path TO tap, public;
SELECT plan(1);
SELECT has_domain( 'new_domain', 'verifichiamo la presenza del domain');
SELECT * FROM finish();
ROLLBACK;
1..1
not ok 1 - verifichiamo la presenza del domain
# Failed test 1: "verifichiamo la presenza del domain"
# Looks like you failed 1 test of 1
Aggiungiamo il domain:
BEGIN;
CREATE DOMAIN new_domain int CHECK (VALUE > 0 AND VALUE < 5);
SET search_path TO tap, public;
SELECT plan(1);
SELECT has_domain( 'new_domain', 'verifichiamo la presenza del domain');
SELECT * FROM finish();
ROLLBACK;
1..1
ok 1 - verifichiamo la presenza del domain
Funziona! Adesso tocca alla tabella:
BEGIN;
CREATE DOMAIN new_domain int CHECK (VALUE > 0 AND VALUE < 5);
SET search_path TO tap, public;
SELECT plan(2);
SELECT has_domain( 'new_domain', 'verifichiamo la presenza del domain');
SELECT has_table( 'new_table', 'verifichiamo la presenza di new_table');
SELECT * FROM finish();
ROLLBACK;
1..2
ok 1 - verifichiamo la presenza del domain
not ok 2 - verifichiamo la presenza di new_table
# Failed test 2: "verifichiamo la presenza di new_table"
# Looks like you failed 1 test of 2
Solita storia: facciamo in modo di passare anche questo secondo test.
BEGIN;
CREATE DOMAIN new_domain int CHECK (VALUE > 0 AND VALUE < 5);
CREATE TABLE new_table (col new_domain);
SET search_path TO tap, public;
SELECT plan(2);
SELECT has_domain( 'new_domain', 'verifichiamo la presenza del domain');
SELECT has_table( 'new_table', 'verifichiamo la presenza di new_table');
SELECT * FROM finish();
ROLLBACK;
Aggiungiamo qualche test “perimetrale”:
BEGIN;
CREATE DOMAIN new_domain int CHECK (VALUE > 0 AND VALUE < 5);
CREATE TABLE new_table (col new_domain);
SET search_path TO tap, public;
SELECT plan(4);
SELECT has_domain( 'new_domain', 'verifichiamo la presenza del domain');
SELECT has_table( 'new_table', 'verifichiamo la presenza di new_table');
SELECT col_type_is('new_table', 'col', 'new_domain');
SELECT col_hasnt_default('new_table', 'col');
SELECT * FROM finish();
ROLLBACK;
Uno degli aspetti interessanti è che possiamo testare l’efficacia della validazione del domain separatamente dal suo uso effettivo in qualche tabella. Nell’esempio che segue creiamo una tabella che ha il solo scopo di testare gli insert:
BEGIN;
CREATE DOMAIN new_domain int CHECK (VALUE > 0 AND VALUE < 5);
CREATE TEMP TABLE dummy (col new_domain);
SET search_path TO tap, public;
SELECT plan(3);
SELECT throws_ok(
'INSERT INTO dummy (col) values(5)',
'23514',
'value for domain new_domain violates check constraint "new_domain_check"',
'Non sono consentiti valori superiori a 4'
);
SELECT throws_ok(
'INSERT INTO dummy (col) values(0)',
'23514',
'value for domain new_domain violates check constraint "new_domain_check"',
'Non sono consentiti valori inferiori a 0'
);
SELECT lives_ok(
'INSERT INTO dummy (col) values(1)',
'1 è un valore consentito'
);
SELECT * FROM finish();
ROLLBACK;
1..3
ok 1 - Non sono consentiti valori superiori a 4
ok 2 - Non sono consentiti valori inferiori a 0
ok 3 - 1 è un valore consentito
Una volta che siamo soddisfatti delle modifiche basterà estrarre gli statement DML e sottometterli, lasciando alla nostra suite di test.