Negli ultimi giorni ho rispolverato le mie conoscenze di PL/SQL, per scrivere dei banali web-service LOW REST invocati da un Interactive Voice Responder, che sfruttano alcune succose funzionalità offerte da Oracle DBMS 10g. Sono tra quelli che credono che 9 volte su 10 non sia cosa buona e giusta scrivere della logica di business nel database, ma in questa situazione ho i miei buoni motivi per farlo (e magari ve ne parlo in un altro post).
Ma veniamo all’argomento del post. Come potete vedere dal logo qui a destra, sono test driven, e quando vi prende il virus, non guarite più
. Ho quindi deciso di programmare a la TDD anche in PL/SQL, anche per la ruggine che si è posata sulla mia memoria.
Le classi (ops volevo dire i package) che implementano xUnit in PL/SQL si possono trovare qui e la libreria si chiama utPLSQL. Installarlo è semplicissimo:
- unzip del contenuto del download in una directory
- dalla directory code dove avete fatto l’unzip lanciate sqlplus sullo schema sul quale volete installare utPLSQL
- da sqlplus: @ut_i_do_install
utplsql non pone alcun vincolo sullo schema sul quale installarlo, l’importante è che l’utente che poi eseguirà i test abbia le grant necessarie a vederlo. E’ invece buona norma (ma non richiesto) che i test case siano nello stesso schema delle procedure da testare.
Questo è quanto! Come potete vedere, lo script ha creato una serie di package, tutti con prefisso ut.
A questo punto scrivere i nostri test di unità è molto semplice. Supponiamo di voler testare la procedure get_id_addetto_by_cell.
CREATE OR REPLACE PACKAGE test_get_id_addetto_by_cell IS PROCEDURE ut_setup; PROCEDURE ut_teardown; PROCEDUREut_get_id_addetto_by_cell; END test_get_id_addetto_by_cell; /
Le due procedure ut_setup e ut_teardown sono i nostri setUp e tearDown. La procedure di test deve avere nome con prefisso ut per poter essere riconosciuta come test case.
CREATE OR REPLACE PACKAGE BODYtest_get_id_addetto_by_cellIS PROCEDURE ut_setup IS BEGIN INSERT INTO R33_AN_ADDETTI (CID, COGNOME, CELLULARE) VALUES ('E-TEST', 'TDD, '123456'); END ut_setup;PROCEDURE ut_teardown IS BEGIN ROLLBACK; END ut_teardown;PROCEDUREut_get_id_addetto_by_cellIS BEGIN utAssert.eq ( 'E-TEST', mobi_query.get_id_addetto_by_cell('123456'), 'E-TEST' ); ENDut_get_id_addetto_by_cell; ENDtest_get_id_addetto_by_cell; /
A questo punto per lanciare il test (da TOAD o da sqlplus):
- exec utplsql.run(‘test_get_id_addetto_by_cell’)
> SSSS U U CCC CCC EEEEEEE SSSS SSSS
> S S U U C C C C E S S S S
> S U U C C C C E S S
> S U U C C E S S
> SSSS U U C C EEEE SSSS SSSS
> S U U C C E S S
> S U U C C C C E S S
> S S U U C C C C E S S S S
> SSSS UUU CCC CCC EEEEEEE SSSS SSSS
.
SUCCESS: "test_get_id_formazione_by_cell"
.
> Individual Test Case Results:
>
SUCCESS - ut_get_id_addetto_by_cell.TEST_RITORNA_CELL: EQ "E-TEST" Expected "E-TEST" and got "E-TEST"
E’ anche possibile creare test suite.
Questo è quanto. Esiste anche un front end grafico per utPLSQL, OUnit, ma non ho avuto modo di provarlo.
TDD rulez!



Una sola cosa suona strana: le procedure ut_setup ed ut_teardown vengono eseguite solo una volta per package di test: al contrario del comportamento di xUnit non racchiudono ogni procedure del package (cioè ogni test case).
In effetti questo può portare ad un’esplosione di package (uno per fixture) da gestire opportunamente…
Commento di Enri — 12 Settembre, 2006 @ 10:39 am
Altra particolarità: se scriviamo più di una assert nel nostro test case, la libreria non si ferma alla prima assert che fallisce, ma procede a valutarle tutte, e a dare nel report del test l’esito di ognuna.
Commento di Enri — 12 Settembre, 2006 @ 12:21 pm
[...] La parte più onerosa sarebbe quella di implementare un piccolo motore che permetta di aggiugere dinamicamente i criteri di ricerca espressi dall’utente nella form. Tuttavia il costrutto ‘EXECUTE IMMEDIATE’ del PL/SQL ci verrebbe in aiuto dando vita di fatto ad un linguaggio di scripting interpretato a runtime (alternativamente ad una soluzione fatta in casa si veda PL/XML). Potremmo poi controllare la complessità accidentale del mondo procedurale programmando alla TDD con utPLSQL. E’ interessante notare come un approccio di questo tipo non impugni la spada degli oggetti per combattere contro il mondo relazionale ma al contrario sfrutta tutte le funzionalità offerte da un RDMBS per cercare una soluzione semplice e mirata al problema. Nella soluzione a cui sto pensando, la BL non è sparsa un po’ sul DB ed un po’ implementata in Java: il PL/SQL con SQLXML ci fornirebbe solo la funzionalità di esporre (quindi in lettura) le nostre tabelle in XML nativamente, così da poter alimentare direttamente la parte di Presentation. Spingendoci un po’ oltre potremmo esporre tali piccoli servizi come webservices (LOW) REST, invocabili direttamente via AJAX, eliminando ogni bisogno di avere un Model “cargo” in Java. [...]
Pingback di ORM con MVC: un fallimento? « Enri Blog — 17 Settembre, 2006 @ 3:22 pm
[...] Quali test effettuare al passo 4.? Anche qui dobbiamo creare dei test di regressione a piccoli ed in modo incrementale, testando sia il “database code” (ad esempio stored procedure, funzioni, trigger, etc), sia gli aspetti più di interfaccia (ad esempio l’esistenza di tabelle, procedure, le definizioni di viste, etc). Stiamo quindi facendo un forte salto in avanti rispetto al solo testing del codice agganciato al db con strumenti quali utPLSQL. [...]
Pingback di Database legacy? Un ricordo del passato con TDDD « Enri Blog — 4 Ottobre, 2006 @ 12:11 pm
> la libreria non si ferma alla >prima assert che fallisce,
Se vuoi farlo basta chiamare la utAssert.eq specificando TRUE nel parametro raise_exc_in.
Complimenti per il blog. Ho sparso “pomodori” per la mia azienda
Commento di Massimo — 17 Novembre, 2006 @ 6:40 pm
Ciao Massimo, grazie per la info e per i complimenti.
Volevo informarti che il blog ha cambiato casa: mi farebbe piacere se mi seguissi su
http://redgreenrefactor.it
Grazie e a presto!
Commento di Enri — 17 Novembre, 2006 @ 8:09 pm