Mondi su mondi, sistemi di sistemi.

pgTAP: un esempio concreto di TDD

Qualche giorno fa par­lavo di pgtap e accen­navo alla pos­si­bi­lità di usarlo per usare il TDD diret­ta­mente con il data­base, sfrut­tando il fatto che PostgreSQL con­sente l’uso di tran­sa­zioni anche per
i comandi di defi­ni­zione delete database.

Questo è un grosso van­tag­gio per­ché posso defi­nire nuovi oggetti dello schema senza che even­tuali errori si pro­pa­ghino al di fuori della ses­sione corrente.

NB: L’esempio che segue è pro­lisso e pro­ba­bil­mente simile a innu­me­re­voli altri esempi sulle virtù del “test infec­ted” ma mi sem­bra comun­que degno di nota che si possa usare que­sto approc­cio anche all’interno di un data­base. 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 impo­stato alcuni para­me­tri per psql, pro­viamo a defi­nire un nuovo domain che verrà poi uti­liz­zato da una nuova tabella.

Ho instal­lato pgtap in uno schema a sé stante, in modo che le sue fun­zioni non si mischino con quelle dello schema “vero”.

Visto che vogliamo svi­lup­pare in moda­lità TDD, creiamo subito il test per veri­fi­care la pre­senza del domain, che ovvia­mente 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 sto­ria: fac­ciamo in modo di pas­sare anche que­sto 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 qual­che 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 inte­res­santi è che pos­siamo testare l’efficacia della vali­da­zione del domain sepa­ra­ta­mente dal suo uso effet­tivo in qual­che 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 sod­di­sfatti delle modi­fi­che basterà estrarre gli sta­te­ment DML e sot­to­met­terli, lasciando alla nostra suite di test.

Per proseguire

Commenti e trackback sono disabilitati.