Construction de Perl, tests unitaires, couverture de code: un exemple de travail complet

86

La plupart des réponses Stackoverflow que j'ai trouvées en ce qui concerne le processus de construction Perl, les tests unitaires et la couverture du code me dirigent simplement vers CPAN pour la documentation. Il n'y a absolument rien de mal à pointer vers les modules CPAN, car c'est là que la documentation complète est censée résider. J'ai eu du mal à trouver des exemples de code de travail complets dans de nombreux cas, cependant.

J'ai recherché partout sur Internet des exemples de code de travail réels que je peux télécharger ou coller dans mon IDE, comme l'exemple de code source de votre didacticiel "Hello World", mais un exemple qui illustre le processus de construction avec des tests unitaires et du code analyse de la couverture. Quelqu'un a-t-il un petit exemple d'un projet de travail complet qui démontre ces technologies et processus?

(J'ai un petit exemple de travail et je répondrai à ma propre question, mais il y a probablement d'autres utilisateurs SO qui ont de meilleurs exemples que ceux que j'ai trouvés.)

Kurt W. Leucht
la source

Réponses:

105

Cela m'a pris un certain temps et il m'a également fallu prendre de petits extraits d'un certain nombre de sources différentes et les fusionner, mais je pense que j'ai un petit exemple de travail qui démontre suffisamment à un débutant Perl le processus de construction de Perl, y compris les tests unitaires et la couverture de code. analyse et reporting. (J'utilise ActiveState ActivePerl v5.10.0 sur un PC Windows XP Pro, Module :: Build , Test :: More , Devel :: Cover )

Commencez avec un répertoire pour votre projet Perl, puis créez un répertoire "lib" et un répertoire "t" sous votre répertoire de projet:

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

Dans le répertoire "lib", créez un fichier texte nommé "HelloPerlBuildWorld.pm". Ce fichier est votre module Perl que vous allez construire et tester. Collez le contenu suivant dans ce fichier:

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;

Dans le répertoire "t", créez un fichier texte nommé "HelloPerlBuildWorld.t". Ce fichier est votre script de test unitaire qui tentera de tester complètement votre module Perl ci-dessus. Collez le contenu suivant dans ce fichier:

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

Maintenant, sauvegardez dans votre répertoire de projet de niveau supérieur, créez un fichier texte nommé "Build.PL". Ce fichier créera vos scripts de construction que vous utiliserez plus tard. Collez le contenu suivant dans ce fichier:

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

Ce sont tous les fichiers dont vous avez besoin. Maintenant, à partir de la ligne de commande dans le répertoire de projet de niveau supérieur, tapez la commande suivante:

perl Build.PL

Vous verrez quelque chose de similaire à ce qui suit:

Checking prerequisites...
Looks good

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

Vous devriez maintenant pouvoir exécuter vos tests unitaires avec la commande suivante:

Build test

Et voyez quelque chose de similaire à ceci:

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)

Pour exécuter vos tests unitaires avec l'analyse de la couverture de code, essayez ceci:

Build testcover

Et vous verrez quelque chose de l'ordre de ceci:

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.

(Quelqu'un, s'il vous plaît, dites-moi comment configurer Cover pour ignorer toutes les bibliothèques Perl sauf et me renvoyer simplement un rapport sur mon seul fichier que j'ai écrit. Je n'ai pas pu faire fonctionner le filtrage de Cover selon la documentation CPAN!)

Maintenant, si vous actualisez votre répertoire de niveau supérieur, vous pouvez voir un nouveau sous-répertoire appelé "cover_db". Allez dans ce répertoire et double-cliquez sur le fichier "coverage.html" pour ouvrir le rapport de couverture de code dans votre navigateur Web préféré. Il vous donne un joli rapport hypertexte à code couleur où vous pouvez cliquer sur le nom de votre fichier et voir les statistiques détaillées de couverture des déclarations, des branches, des conditions et des sous-programmes pour votre module Perl dans le rapport à côté du code source réel. Vous pouvez voir dans ce rapport que nous n'avons pas du tout couvert la routine "bye ()" et qu'il y a aussi une ligne de code inaccessible qui n'a pas été couverte comme prévu.

instantané du rapport de couverture de code
(source: leucht.com )

Une autre chose que vous pouvez faire pour aider à automatiser ce processus dans votre IDE est de créer d'autres fichiers de type "Build.PL" qui exécutent explicitement certaines des cibles de construction que nous avons faites ci-dessus manuellement à partir de la ligne de commande. Par exemple, j'utilise un fichier "BuildTest.PL" avec le contenu suivant:

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

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

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

Ensuite, j'ai configuré mon IDE pour exécuter ce fichier (via "perl BuiltTest.PL") avec un simple clic de souris et il exécute automatiquement mon code de test unitaire à partir de l'EDI au lieu de le faire manuellement à partir de la ligne de commande. Remplacez «dispatch ('test')» par «dispatch ('testcover')» pour une exécution automatisée de la couverture de code. Tapez "Aide à la construction" pour une liste complète des cibles de construction disponibles à partir de Module :: Build.

Kurt W. Leucht
la source
1
Votre idée de mettre en place un BuiltTest.PL ne me semble pas bonne. Pourquoi ne pouvez-vous pas simplement écrire un script qui fait Build buildet ensuite Build test?
Leon Timmermans
2
Leon, suggérez-vous un script perl qui effectue des appels en ligne de commande? Si tel est le cas, je préfère ne pas faire d'appels de ligne de commande s'il existe un moyen OO de faire les appels par programme, comme dans l'exemple de fichier BuiltTest.PL.
Kurt W. Leucht
1
Ce n'est pas nécessaire, voir ma propre réponse
Leon Timmermans
2
Module :: Build n'est tout simplement pas pour CPAN. Vous pouvez toujours obtenir toutes les fonctionnalités des différents outils CPAN même si ce n'est pas sur CPAN. Vous pouvez toujours le construire, le tester, le distribuer et l'installer avec le même processus même s'il s'agit d'un module privé.
brian d foy le
4
Pour filtrer les résultats dans Devel :: Cover, j'ajoute des options $ENV{HARNESS_PERL_SWITCHES}. Par exemple: -MDevel::Cover=+ignore,.t$,+inc,/app/lib,-select,MyModule.pm/app/libest la bibliothèque privée de l'application et MyModule.pmle module à tester.
Michael Carman
14

En réponse à Kurt, je proposerais cette alternative à son 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');

Il réutilise la base de données construite par Build.PL (et suppose donc qu'elle a déjà été exécutée).

Léon Timmermans
la source
Parfait! Merci, Leon. Je savais que quelque chose n'allait pas avec mon exemple, mais je suis encore nouveau dans ce truc de construction perl moi-même! :-)
Kurt W. Leucht
12

L' module-starterutilité fantastique génère un projet squelette facile à utiliser qui gère l'installation du module, la création de la documentation et une bonne mise en page pour les fichiers de module à vivre, et - je pense - le support de la couverture du code. C'est IMO un bon début pour toute entreprise liée au module Perl.

Aussi: utiliser des outils liés au CPAN comme Module::Build- même pour des modules qui ne seront probablement jamais publiés publiquement - est une très bonne idée .

Gaurav
la source
7

(divulgation: je suis l'auteur)

Une fois que tout est trié comme décrit ci-dessus, vous pouvez passer à l'étape suivante et utiliser Devel :: CoverX :: Covered pour par exemple

  • Étant donné un fichier source, répertoriez les fichiers de test qui fournissent une couverture à ce fichier source. Cela peut être fait au niveau d'un fichier, d'une sous-routine et d'une ligne.
  • Étant donné un fichier de test, répertoriez les fichiers source et les sous-marins couverts par ce fichier de test.
  • Étant donné un fichier source, faites un rapport efficace sur les détails de la couverture par ligne ou sous.

Voir le synopsis pour des exemples concrets de ligne de commande.

Dans Devel :: PerlySense , Emacs prend en charge l'affichage des informations de couverture dans le tampon du code source ( capture d'écran ), et pour naviguer vers / depuis les fichiers de test de couverture.

jplindstrom
la source