| |||
| © Perl Mongers Italia. Tutti i diritti riservati. | |||
Lo sviluppo di un software, a qualunque livello non banale, comporta normalmente differenti cicli o, comunque, differenti fasi. Quante, e soprattutto quali, siano queste fasi è stato negli anni precedenti oggetto di un vivace dibattito, dal quale sembrano essere uscite vincenti le tecniche di sviluppo agile (agile development) la più nota delle quali è probabilmente quella che va sotto il nome di XP - eXtreme Programming. Quello su cui probabilmente c'è accordo è il fatto che il software ha bisogno di essere sottoposto continuamente a verifiche. Questo è qualcosa che chiunque con un minimo di esperienza di programmazione ha provato da sé: facciamo un po' di codice e proviamo se funziona. Mentre però potremmo essere portati ad un approccio un po' naive a questo tipo di prove, facendole magari con un po' di fretta e dimenticandocene una volta che va tutto bene, il buon senso punta ad avere un approccio un po' più sistematico ed organizzare questi test per poterli ripetere con regolarità. Da tutto ciò nasce il concetto di test suite, ossia di quell'insieme di strumenti (normalmente script) predisposti unicamente alla verifica che le differenti parti del software stiano funzionando come ci si aspetta. Tali test possono essere divisi in più parti: dovremmo infatti avere test per ogni singola parte del software (ogni modulo, ad esempio), e test più ampi, che verifichino l'integrazione dei moduli fra di loro. Scrivere i test non è un lavoro facile, e potrebbe risultare noioso perché, in realtà, si sta sviluppando qualcosa che non serve direttamente al problema che il software dovrebbe risolvere. Il nostro problema, però, non è lo stesso del software. Se stiamo realizzando un processore di testo, il software deve processare il testo (sic!), noi dobbiamo sviluppare il software. Per questo motivo, dobbiamo mettere nella faretra tutte le frecce che sono necessarie affinché questo lavoro di sviluppo sia semplice, il più possibile privo di errori e, soprattutto, manutenibile in futuro. Perché una cosa è sicura: i bug ci saranno, per sottili e insignificanti che possano apparire. Quando scrivere i test?L'approccio XP prevede che i test siano scritti prima di scrivere il relativo pezzo di software. Può sembrare strano, ma è così. In realtà, si va molto oltre: occorre codificare solo ciò che permette di passare i test. In pratica:
Secondo questa definizione, una feature non esiste se non c'è un test che la verifichi. Devo ammettere che ancora non mi sono abituato a questo stile: di solito sono molto più curioso di scrivere il codice per vedere se risolve il problema, e lascio i test a dopo. Probabilmente, però, sbaglio: a quel punto, scrivere i test diventa una noia mortale! Come organizzare i test in Perl?Come detto prima, i test dovrebbero cercare di coprire il più possibile quanto state sviluppando. In generale, se scrivete un po' di codice che non è testabile, dovreste ripensarlo per fare in modo che lo sia; questo vi risparmierà tempo e fatica quando salterà fuori qualche bug in futuro. Perl mette a disposizione una serie di moduli preposti specificamente alle attività di test. Qui vedremo solamente due di essi, peraltro molto legati: Test::Simple ed il più evoluto Test::More. La base: Test::SimpleOrmai nessuno, probabilmente, utilizza più Test::Simple per un nuovo progetto. Per il semplice motivo che è troppo Simple. In ogni caso, per partire va più che bene. La prima cosa da fare è... usarlo: use Test::Simple tests => 3; In fase di utilizzo, dobbiamo dire quanti test abbiamo intenzione di fare. Perché? È piuttosto semplice: per avere un riscontro quando, per qualche motivo, lo script sotto test muore prima del tempo. Se lo fa, vuol dire che effettua meno test e possiamo controllarlo: #!/usr/bin/perl
use strict;
use warnings;
use Test::Simple tests => 3;
ok( 2 + 3 == 5, "somma");
ok( 2 * 2 == 5, "moltiplicazione");
die("non voglio!");
ok( 6 / 3 == 2, "divisione");
__END__
1..3
ok 1 - somma
not ok 2 - moltiplicazione
# Failed test (simple01.pl at line 7)
non voglio! at simple01.pl line 8.
# Looks like you planned 3 tests but only ran 2.
# Looks like your test died just after 2.
Stiamo dichiarando 3 test, solo che chiamiamo
Cosa succede quando va tutto fino in fondo, invece? Presto detto: #!/usr/bin/perl use strict; use warnings; use Test::Simple tests => 3; ok( 2 + 3 == 5, "somma"); ok( 2 * 2 == 5, "moltiplicazione"); ok( 6 / 3 == 2, "divisione"); __END__ 1..3 ok 1 - somma not ok 2 - moltiplicazione # Failed test (simple02.pl at line 7) ok 3 - divisione # Looks like you failed 1 tests of 3. Piuttosto semplice: ci stampa una riga finale di riassunto. Nel caso vada tutto bene è più silenzioso: #!/usr/bin/perl use strict; use warnings; use Test::Simple tests => 3; ok( 2 + 3 == 5, "somma"); ok( 2 * 2 == 4, "moltiplicazione"); ok( 6 / 3 == 2, "divisione"); __END__ 1..3 ok 1 - somma ok 2 - moltiplicazione ok 3 - divisione Come a dire: nessuna nuova, buona nuova. Siamo già in grado di scrivere i test! Mettere le briglie ai testSe abbiamo un solo file di test, tutto procede abbastanza semplicemente: basta lanciarlo ed aspettare di vedere se c'è un rapporto finale oppure no. Quando però i test diventano parecchi, c'è il rischio di perdersi se qualche file ha dato errore oppure no; per questo motivo è stato creato Test::Harness, il modulo che mette le briglie ai test. La cosa bella è che, in generale, non avrete bisogno di utilizzarlo direttamente: se fate partire i vostri
progetti di nuovi moduli utilizzando Per chiamare tutti insieme i nostri script (supponendo siano tutti i "pl" nella directory corrente): perl "-MTest::Harness" "-e" "runtests(@ARGV)" *.pl Recuperando i test introdotti in precedenza otteniamo quanto segue: simple01....# Failed test (simple01.pl at line 7)
non voglio! at simple01.pl line 8.
# Looks like you planned 3 tests but only ran 2.
# Looks like your test died just after 2.
simple01....dubious
Test returned status 255 (wstat 65280, 0xff00)
DIED. FAILED tests 2-3
Failed 2/3 tests, 33.33% okay
simple02....# Failed test (simple02.pl at line 7)
# Looks like you failed 1 tests of 3.
simple02....dubious
Test returned status 1 (wstat 256, 0x100)
DIED. FAILED test 2
Failed 1/3 tests, 66.67% okay
simple03....ok
Failed Test Stat Wstat Total Fail Failed List of Failed
-------------------------------------------------------------------------------
simple01.pl 255 65280 3 3 100.00% 2-3
simple02.pl 1 256 3 1 33.33% 2
Failed 2/3 test scripts, 33.33% okay. 3/9 subtests failed, 66.67% okay.
L'uscita è meno loquace rispetto a prima: in particolare, vengono riportate solamente le condizioni di errore
trovate o, in caso di successo, un semplice La parte interessante si ha però nelle statistiche finali. Queste sono quello che dà il vero colpo d'occhio sui test: quanti sono andati bene? Quanti male, e dove? Bene, questa tabellina ci dà un volo d'uccello sui test eseguiti, mettendoci in condizione di approfondire nel caso qualcosa vada male. Andiamo avanti: Test::MoreSe avete giocato un po' con Test::Simple (l'avete fatto, vero?) avrete notato che molte cose le dovete ancora fare voi. Inoltre, i test non sono proprio leggibilissimi: occorre associare ad ogni test una condizione booleana, il che appiattisce i test. Bene, allora, cosa si testa di solito? Proviamo a fare una rassegna:
A tutto ciò viene incontro Test::More. Prima di tutto, però, inventiamoci un modulo completamente inutile da testare: package Inutile;
use strict;
use warnings;
require Exporter;
our @ISA = qw( Exporter );
our @EXPORT = qw( sempre_vera sempre_falsa per_due in_hash );
sub sempre_vera { return 1 }
sub sempre_falsa { return 0 }
sub per_due { return 2 * $_[0] }
sub in_hash { return { @_ } }
1;
È un modulo semplice ed inutile, come indica chiaramente il nome. Vanta ben 1e12 tentativi di imitazione, comunque. Per semplicità esporta tutte le funzioni contenute (e questa non è di solito una buona idea), che sono:
Il file di test relativo a questo modulo è il seguente: #!/usr/bin/perl
use Test::More 'no_plan';
BEGIN {
use_ok('Inutile');
}
# Test per un valore vero
ok(sempre_vera(), "funzione sempre_vera()");
# Test per un valore falso
ok(!sempre_falsa(), "funzione sempre_falsa()");
# Test che il valore restituito sia quello atteso
is(per_due(5), 10, "per_due()");
isnt(per_due(3), 7, "per_due(), test negativo");
# Test su una struttura
is_deeply(in_hash('ciao', 'a', 'tutti', 'quanti'),
{ciao => 'a', tutti => 'quanti'}, 'in_hash()');
Si noti che, in fase di In dettaglio:
Insomma, di frecce ne abbiamo ora, basta allenarsi e saremo anche noi dei piccoli Robin Hood. ConcludendoSpero che questa breve panoramica sul test vi abbia convinto che scrivere i test è una cosa utile e, in Perl, relativamente semplice. Test::More va ovviamente oltre quanto brevemente descritto sopra, un'occhiata al manuale vi convincerà. Se poi non ne avete abbastanza, CPAN contiene miriadi di moduli per il testing - c'è solo l'imbarazzo della scelta... | |||