Perl build, unit test, code coverage: un esempio funzionante completo


86

La maggior parte delle risposte di Stackoverflow che ho trovato riguardo al processo di compilazione Perl, al test di unità e alla copertura del codice mi indirizzano semplicemente a CPAN per la documentazione. Non c'è assolutamente niente di sbagliato nel puntare ai moduli CPAN perché è lì che dovrebbe risiedere la documentazione completa. Tuttavia, in molti casi ho avuto problemi a trovare esempi di codice funzionanti completi.

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

(Ho un piccolo esempio funzionante e risponderò alla mia domanda con esso, ma probabilmente ci sono altri utenti SO che hanno esempi migliori di quelli che ho trovato.)

Risposte:


105

Mi ci è voluto un po 'e mi ci è voluto anche prendere piccoli frammenti da un numero di fonti diverse e fonderli insieme, ma penso di avere un piccolo esempio funzionante che dimostra sufficientemente a un principiante di Perl il processo di compilazione Perl inclusi test di unità e copertura del codice analisi e reportistica. (Sto usando ActiveState ActivePerl v5.10.0 su un PC Windows XP Pro, Module :: Build , Test :: More , Develop :: Cover )

Inizia con una directory per il tuo progetto Perl e poi crea una directory "lib" e una directory "t" nella directory del tuo progetto:

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

Nella directory "lib", crea un file di testo denominato "HelloPerlBuildWorld.pm". Questo file è il tuo modulo Perl che costruirai e testerai. Incolla 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", crea un file di testo denominato "HelloPerlBuildWorld.t". Questo file è il tuo script di unit test che tenterà di testare completamente il tuo modulo Perl sopra. Incolla 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 esegui il backup nella directory del progetto di livello superiore, crea un file di testo denominato "Build.PL". Questo file creerà i tuoi script di build che utilizzerai 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_addy@goes.here>',
    build_requires => {
        'Test::More' => '0.10',
    },
);

$builder->create_build_script();

Sono tutti i file di cui hai bisogno. Ora dalla riga di comando nella directory del progetto di livello superiore, digita il seguente comando:

perl Build.PL

Vedrai qualcosa di simile al seguente:

Checking prerequisites...
Looks good

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

Ora dovresti essere in grado di eseguire i tuoi unit test con il seguente comando:

Build test

E vedi 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 gli unit test con l'analisi della copertura del codice, prova questo:

Build testcover

E vedrai qualcosa nell'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 configurare Cover per ignorare tutte le librerie Perl eccetto e riferirmi semplicemente sul mio singolo file che ho scritto. Non sono riuscito a far funzionare il filtro Cover secondo la documentazione CPAN!)

Ora, se aggiorni la tua directory di primo livello, puoi vedere una nuova sottodirectory chiamata "cover_db". Andare in quella directory e fare doppio clic sul file "coverage.html" per aprire il report sulla copertura del codice nel browser Web preferito. Fornisce un bel rapporto ipertestuale codificato a colori in cui è possibile fare clic sul nome del file e vedere le statistiche dettagliate sulla copertura di dichiarazione, ramo, condizione e subroutine per il modulo Perl proprio lì nel rapporto accanto al codice sorgente effettivo. Puoi vedere in questo rapporto che non abbiamo trattato affatto la routine "bye ()" e che c'è anche una riga di codice irraggiungibile che non è stata trattata come ci aspettavamo.

istantanea del rapporto sulla copertura del codice
(fonte: leucht.com )

Un'altra cosa che puoi fare per aiutare ad automatizzare questo processo nel tuo IDE è creare altri file di tipo "Build.PL" che eseguono esplicitamente alcuni degli obiettivi di compilazione che abbiamo fatto sopra manualmente dalla riga di comando. Ad esempio, utilizzo 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');

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


1
La tua idea di creare un BuiltTest.PL non mi suona bene. Perché non puoi semplicemente scrivere una sceneggiatura che lo fa Build builde poi Build test?
Leon Timmermans

2
Leon, stai suggerendo uno script Perl che effettui chiamate dalla riga 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 BuiltTest.PL di esempio.
Kurt W. Leucht

1
Non è necessario, vedi la mia risposta
Leon Timmermans

2
Module :: Build non è solo per CPAN. Puoi comunque ottenere tutte le funzionalità dai vari strumenti CPAN anche se non è su CPAN. Puoi ancora crearlo, testarlo, distribuirlo e installarlo con lo stesso processo anche se è un modulo privato.
brian d foy

4
Per filtrare i risultati in Develop :: Cover aggiungo opzioni a $ENV{HARNESS_PERL_SWITCHES}. Ad esempio: -MDevel::Cover=+ignore,.t$,+inc,/app/lib,-select,MyModule.pmdov'è /app/libla libreria privata dell'applicazione ed MyModule.pmè il modulo in prova.
Michael Carman

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');

Riutilizza il database compilato da Build.PL (e quindi presume che sia già stato eseguito).


Perfetto! Grazie, Leon. Sapevo che c'era qualcosa che non andava nel mio esempio, ma sono ancora nuovo a questa roba di build perl! :-)
Kurt W. Leucht


12

L'utilità fantastica module-startergenera un progetto scheletro facile da usare che gestisce l'installazione dei moduli, la creazione di documentazione e un buon layout per i file dei moduli in cui vivere e, credo , il supporto per la copertura del codice. IMO è un ottimo inizio per qualsiasi attività relativa ai moduli Perl.

Inoltre: l'utilizzo di strumenti relativi a CPAN come Module::Build- anche per moduli che probabilmente non verranno mai rilasciati pubblicamente - è un'ottima idea .


7

(divulgazione: sono l'autore)

Una volta che hai ordinato tutto come descritto sopra, puoi fare il passo successivo e usare Devel :: CoverX :: Covered per es

  • Dato un file di origine, elenca i file di test che forniscono copertura a quel file di origine. Questo può essere fatto a livello di file, sotto routine e riga.
  • Dato un file di prova, elenca i file sorgente e i sottotitoli coperti da quel file di prova.
  • Dato un file di origine, segnalare in modo efficiente i dettagli di copertura per riga o sottotitoli.

Vedere la sinossi per esempi concreti della riga di comando.

In Develop :: PerlySense c'è il supporto Emacs per visualizzare le informazioni di copertura nel buffer del codice sorgente ( screenshot ) e per navigare verso / da i file di test di copertura.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.