88 votes

Construction en Perl, tests unitaires, couverture de code : Un exemple fonctionnel complet

La plupart des réponses de Stackoverflow que j'ai trouvées en ce qui concerne le processus de construction de Perl, les tests unitaires et la couverture du code me renvoient simplement au CPAN pour la documentation qui s'y trouve. 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 complets et fonctionnels dans de nombreux cas, cependant.

J'ai cherché partout sur Internet des exemples de code fonctionnel que je puisse télécharger ou coller dans mon IDE, comme le code source de l'exemple typique de tutoriel "Hello World", mais d'un exemple qui démontre le processus de construction avec des tests unitaires et une analyse de la couverture du code. Est-ce que quelqu'un a un petit exemple d'un projet de travail complet qui démontre ces technologies et processus ?

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

110voto

Kurt W. Leucht Points 2176

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

Commencez par 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 test unitaire script 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, dans le répertoire supérieur de votre projet, 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_addy@goes.here>',
    build_requires => {
        'Test::More' => '0.10',
    },
);

$builder->create_build_script();

C'est tous les fichiers dont vous avez besoin. Maintenant, à partir de la ligne de commande dans le répertoire de premier niveau du projet, 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 être en mesure d'exécuter vos tests unitaires avec la commande suivante :

Build test

Et voir 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 une analyse de la couverture du 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 pourrait-il me dire comment configurer Cover pour qu'il ignore toutes les bibliothèques Perl, à l'exception du fichier que j'ai écrit ? Je n'ai pas réussi à faire fonctionner le filtrage de Cover selon la documentation CPAN).

Maintenant, si vous rafraîchissez votre répertoire de premier niveau, 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é. Vous obtenez un joli rapport hypertexte codé en couleur dans lequel vous pouvez cliquer sur le nom de votre fichier et voir les statistiques détaillées de couverture des instructions, des branches, des conditions et des sous-routines pour votre module Perl, directement 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 également une ligne de code inaccessible qui n'a pas été couverte comme prévu.

snapshot of code coverage report
(source : <a href="http://www.leucht.com/images/CodeCoverageExample.jpg" rel="noreferrer">leucht.com </a>)

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, je configure mon IDE pour qu'il exécute ce fichier (via "perl BuiltTest.PL") d'un simple clic de souris et il exécute automatiquement mon code de test unitaire depuis l'IDE au lieu que je le fasse manuellement depuis la ligne de commande. Remplacez le "dispatch('test')" par "dispatch('testcover')" pour une exécution automatisée de la couverture de code. Tapez "Build help" pour obtenir une liste complète des cibles de construction disponibles dans Module::Build.

1 votes

Votre idée de mettre en place un BuiltTest.PL ne me semble pas bonne. Pourquoi ne pas simplement écrire un script qui fait Build build et ensuite Build test ?

2 votes

Leon, suggérez-vous un perl script qui fait des appels en ligne de commande ? Si c'est le cas, je préférerais ne pas faire d'appels en ligne de commande s'il existe un moyen OO de faire les appels de manière programmatique comme dans l'exemple du fichier BuiltTest.PL.

1 votes

Ce n'est pas nécessaire, voir ma propre réponse.

14voto

Leon Timmermans Points 23230

En réponse à Kurt, je propose cette alternative à son script de 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).

0 votes

Parfait ! Merci, Leon. Je savais que quelque chose n'allait pas dans mon exemple, mais je suis encore novice dans le domaine de la construction en perl ! :-)

12voto

Gaurav Points 1064

L'aide fantastique module-starter génère un projet de squelette facile à utiliser qui prend en charge l'installation des modules, la création de la documentation et d'une bonne mise en page pour les fichiers de modules, et -- je pensez à -- support de la couverture du code. C'est un excellent point de départ pour tout projet lié aux modules Perl.

De plus, l'utilisation d'outils liés à CPAN comme Module::Build -- même pour des modules qui ne seront probablement jamais publiés est une très bonne idée .

12voto

brian d foy Points 71781

J'en parle dans Perl intermédiaire ainsi que Maîtriser Perl . Kurt, cependant, a donné un bon résumé.

Je combine tout cela dans une version script utilisant Module::Release cependant. Je tape une commande et tout se passe.

7voto

jplindstrom Points 563

(divulgation : je suis l'auteur)

Une fois que vous avez tout trié comme décrit ci-dessus, vous pouvez passer à l'étape suivante et utiliser la méthode suivante Devel::CoverX::Couvert par exemple

  • Étant donné un fichier source, listez les fichiers de test qui fournissent une couverture à ce fichier source. Ceci peut être fait au niveau du fichier, de la sous routine et de la ligne.
  • Étant donné un fichier de test, listez les fichiers sources et les sous-fichiers 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 en ligne de commande.

Sur Devel::PerlySense il existe un support Emacs pour afficher les informations de couverture dans le tampon du code source ( capture d'écran ), et pour naviguer vers/depuis les fichiers de test de couverture.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X