81

La maggior parte delle risposte StackOverflow che ho trovato riguardo al processo di compilazione Perl e al test dell'unità e alla copertura del codice mi indicano semplicemente il CPAN per la documentazione. Non c'è assolutamente nulla di sbagliato nel puntare ai moduli CPAN perché è lì che risiede la documentazione completa. Tuttavia, ho avuto difficoltà a trovare esempi completi di codice funzionante in molti casi.Perl build, unit test, copertura del codice: un esempio operativo completo

Ho cercato su Internet tutti gli esempi di codice funzionante che posso scaricare o incollare nel mio IDE, come il tipico esempio del codice sorgente di esempio "Hello World", ma di un esempio che dimostra il processo di costruzione con test di unità e analisi della copertura del codice. Qualcuno ha un piccolo esempio di un progetto di lavoro completo che dimostra queste tecnologie e questi processi?

(ho un piccolo esempio di lavoro e io risponderò alla mia domanda con esso, ma ci sono probabilmente altri utenti in modo che hanno gli esempi migliori di quelli che mi si avvicinò con.)

risposta

101

Ci sono voluti un po 'di tempo e mi è anche servito prendere piccoli frammenti da diverse fonti e fonderli insieme, ma penso di avere un piccolo esempio funzionante che dimostra sufficientemente a un principiante Perl il processo di costruzione Perl che include test di unità e analisi della copertura del codice & segnalazione. (Sto usando ActiveState ActivePerl v5.10.0 su un PC Windows XP Pro, Module::Build, Test::More, Devel::Cover)

iniziare con una directory per il vostro progetto Perl e quindi creare una directory "lib" e una directory "t" sotto directory del progetto:

HelloPerlBuildWorld 
     | 
     |----------> lib 
     | 
     |----------> t 

Nella directory "lib", creare un file di testo denominato "HelloPerlBuildWorld.pm". Questo file è il tuo modulo Perl che costruirai e testerai. Incollare il seguente contenuto in questo file:

use strict; 
use warnings; 
package HelloPerlBuildWorld; 

$HelloPerlBuildWorld::VERSION = '0.1'; 

sub hello { 
    return "Hello, Perl Build World!"; 
} 

sub bye { 
    return "Goodbye, cruel world!"; 
} 

sub repeat { 
    return 1; 
} 

sub argumentTest { 
    my ($booleanArg) = @_; 

    if (!defined($booleanArg)) { 
     return "null"; 
    } 
    elsif ($booleanArg eq "false") { 
     return "false"; 
    } 
    elsif ($booleanArg eq "true") { 
     return "true"; 
    } 
    else { 
     return "unknown"; 
    } 

    return "Unreachable code: cannot be covered"; 
} 

1; 

Nella directory "t", creare un file di testo denominato "HelloPerlBuildWorld.t". Questo file è lo script di test dell'unità che tenterà di testare completamente il modulo Perl sopra. Incollare il seguente contenuto in questo file:

use strict; 
use warnings; 
use Test::More qw(no_plan); 

# Verify module can be included via "use" pragma 
BEGIN { use_ok('HelloPerlBuildWorld') }; 

# Verify module can be included via "require" pragma 
require_ok('HelloPerlBuildWorld'); 

# Test hello() routine using a regular expression 
my $helloCall = HelloPerlBuildWorld::hello(); 
like($helloCall, qr/Hello, .*World/, "hello() RE test"); 

# Test hello_message() routine using a got/expected routine 
is($helloCall, "Hello, Perl Build World!", "hello() IS test"); 

# Do not test bye() routine 

# Test repeat() routine using a got/expected routine 
for (my $ctr=1; $ctr<=10; $ctr++) { 
    my $repeatCall = HelloPerlBuildWorld::repeat(); 
    is($repeatCall, 1, "repeat() IS test"); 
} 

# Test argumentTest() 
my $argumentTestCall1 = HelloPerlBuildWorld::argumentTest(); 
is($argumentTestCall1, "null", "argumentTest() IS null test"); 

# Test argumentTest("true") 
my $argumentTestCall2 = HelloPerlBuildWorld::argumentTest("true"); 
is($argumentTestCall2, "true", "argumentTest() IS true test"); 

# Test argumentTest("false") 
my $argumentTestCall3 = HelloPerlBuildWorld::argumentTest("false"); 
is($argumentTestCall3, "false", "argumentTest() IS false test"); 

# Test argumentTest(123) 
my $argumentTestCall4 = HelloPerlBuildWorld::argumentTest(123); 
is($argumentTestCall4, "unknown", "argumentTest() IS unknown test"); 

Ora torna nella vostra directory principale del progetto di livello, creare un file di testo denominato "Build.PL". Questo file creerà gli script di build che userete in seguito. Incolla il seguente contenuto in questo file:

use strict; 
use warnings; 
use Module::Build; 

my $builder = Module::Build->new(
    module_name   => 'HelloPerlBuildWorld', 
    license    => 'perl', 
    dist_abstract  => 'HelloPerlBuildWorld short description', 
    dist_author   => 'Author Name <[email protected]>', 
    build_requires => { 
     'Test::More' => '0.10', 
    }, 
); 

$builder->create_build_script(); 

Questi sono tutti i file necessari.Ora dalla riga di comando nella directory principale del progetto di livello, digitare il seguente comando:

perl Build.PL 

Si vedrà qualcosa di simile al seguente:

Checking prerequisites... 
Looks good 

Creating new 'Build' script for 'HelloPerlBuildWorld' version '0.1' 

Ora si dovrebbe essere in grado di eseguire il test di unità con il seguente comando:

Build test 

E vedere qualcosa di simile a questo:

Copying lib\HelloPerlBuildWorld.pm -> blib\lib\HelloPerlBuildWorld.pm 
t\HelloPerlBuildWorld....ok 
All tests successful. 
Files=1, Tests=18, 0 wallclock secs (0.00 cusr + 0.00 csys = 0.00 CPU) 

Per eseguire il test di unità con l'analisi della copertura del codice, provate questo:

Build testcover 

E vedrete qualcosa sull'ordine di questo:

t\HelloPerlBuildWorld....ok 
All tests successful. 
Files=1, Tests=18, 12 wallclock secs (0.00 cusr + 0.00 csys = 0.00 CPU) 
cover 
Reading database from D:/Documents and Settings/LeuchKW/workspace/HelloPerlBuildWorld/cover_db 


----------------------------------- ------ ------ ------ ------ ------ ------ 
File         stmt bran cond sub time total 
----------------------------------- ------ ------ ------ ------ ------ ------ 
D:/Perl/lib/ActivePerl/Config.pm  0.0 0.0 0.0 0.0 n/a 0.0 
D:/Perl/lib/ActiveState/Path.pm  0.0 0.0 0.0 0.0 n/a 0.0 
D:/Perl/lib/AutoLoader.pm    0.0 0.0 0.0 0.0 n/a 0.0 
D:/Perl/lib/B.pm      18.6 16.7 13.3 19.2 96.4 17.6 
... 
[SNIP] 
... 
D:/Perl/lib/re.pm      0.0 0.0 0.0 0.0 n/a 0.0 
D:/Perl/lib/strict.pm     84.6 50.0 50.0 100.0 0.0 73.1 
D:/Perl/lib/vars.pm     44.4 36.4 0.0 100.0 0.0 36.2 
D:/Perl/lib/warnings.pm    15.3 12.1 0.0 11.1 0.0 12.0 
D:/Perl/lib/warnings/register.pm  0.0 0.0 n/a 0.0 n/a 0.0 
blib/lib/HelloPerlBuildWorld.pm  87.5 100.0 n/a 83.3 0.0 89.3 
Total         9.9 4.6 2.8 11.3 100.0 7.6 
----------------------------------- ------ ------ ------ ------ ------ ------ 


Writing HTML output to D:/Documents and Settings/LeuchKW/workspace/HelloPerlBuildWorld/cover_db/coverage.html ... 
done. 

(Qualcuno per favore mi dica come per configurare Cover in modo che ignori tutte le librerie Perl tranne che mi facciano rapporto sul mio singolo file che ho scritto. Non ho potuto ottenere il filtro di copertura per funzionare secondo la documentazione CPAN!)

Ora se si aggiorna la directory di primo livello, è possibile vedere una nuova sottodirectory chiamata "cover_db". Vai in quella directory e fai doppio clic sul file "coverage.html" per aprire il report sulla copertura del codice nel tuo browser web preferito. Ti dà un bel rapporto ipertestuale con codice colore in cui puoi cliccare sul nome del tuo file e vedere le dichiarazioni dettagliate, ramo, condizione, statistiche di copertura di subroutine per il tuo modulo Perl proprio lì nel rapporto accanto al codice sorgente reale. In questo rapporto puoi vedere che non abbiamo affatto trattato la routine "bye()" e inoltre c'è una riga di codice irraggiungibile che non è stata coperta come previsto.

snapshot of code coverage report http://www.leucht.com/images/CodeCoverageExample.jpg

Una cosa si può fare per aiutare ad automatizzare questo processo nel vostro IDE è di fare qualche altro file di tipo "Build.PL" che eseguono esplicitamente alcuni degli obiettivi di build che abbiamo fatto in precedenza manualmente dal riga di comando. Ad esempio, io uso un file "BuildTest.PL" con il seguente contenuto:

use strict; 
use warnings; 
use Module::Build; 

my $build = Module::Build->resume (
    properties => { 
    config_dir => '_build', 
    }, 
); 

$build->dispatch('build'); 
$build->dispatch('test'); 

Poi ho impostato il mio IDE per eseguire questo file (tramite "perl BuiltTest.PL") con un semplice clic del mouse e esegue automaticamente il mio codice di test dell'unità dall'IDE invece di farlo manualmente dalla riga di comando. Sostituire "dispatch ('test')" con "dispatch ('testcover')" per l'esecuzione automatica della copertura del codice. Digitare "Build help" per un elenco completo di destinazioni di build disponibili da Module :: Build.

+1

La tua idea di creare un BuiltTest.PL non mi sembra buona. Perché non puoi semplicemente scrivere uno script che comporti 'Build build' e quindi' Build test'? –

+2

Leon, stai suggerendo uno script perl che effettua chiamate da linea di comando? In tal caso, preferirei non effettuare chiamate dalla riga di comando se esiste un modo OO per effettuare le chiamate a livello di programmazione come nel file di esempio BuiltTest.PL. –

+1

Non è necessario, vedere la mia risposta –

14

In risposta a Kurt, proporrei questa alternativa al suo script BuiltTest.PL.

use strict; 
use warnings; 
use Module::Build; 

my $build = Module::Build->resume (
    properties => { 
    config_dir => '_build', 
    }, 
); 

$build->dispatch('build'); 
$build->dispatch('test'); 

Riusa la build del database da Build.PL (e quindi presuppone che sia già in esecuzione).

+0

Perfetto! Grazie, Leon. Sapevo che c'era qualcosa di sbagliato nel mio esempio, ma sono ancora nuovo in questa roba perl build! :-) –

12

Il incredibilmente disponibile module-starter genera un progetto scheletro facile da usare che gestisce l'installazione del modulo, la creazione di documentazione e un buon layout per i file di moduli in cui vivere, e - io penso - supporto copertura del codice. È IMO un ottimo inizio per qualsiasi attività relativa al modulo Perl.

Inoltre: utilizzo di strumenti correlati a CPAN come Module::Build - anche per i moduli che probabilmente non saranno mai rilasciati pubblicamente - is a very good idea.

7

(disclosure: io sono l'autore)

Una volta che avete tutto ordinato come descritto sopra, si potrebbe fare il passo successivo e utilizzare Devel::CoverX::Covered per esempio

  • Dato un file di origine, elencare i file di test che forniscono copertura a tale file di origine. Questo può essere fatto su un file, sottofamiglia e livello di riga.
  • Dato un file di prova, elencare i file di origine ei sottotitoli coperti da tale file di test.
  • Dato un file di origine, riportare in modo efficiente sui dettagli di copertura per riga o sotto.

Vedere synopsis per esempi di riga di comando concreti.

In Devel::PerlySense è disponibile il supporto di Emacs per visualizzare le informazioni sulla copertura nel buffer del codice sorgente (screen shot) e per spostarsi verso/dalla copertura dei file di test.