Perl-Build, Unit-Test, Codeabdeckung: Ein vollständiges Arbeitsbeispiel

85

Die meisten Stackoverflow-Antworten, die ich in Bezug auf den Perl-Erstellungsprozess und das Testen von Einheiten sowie die Codeabdeckung gefunden habe, verweisen mich einfach auf CPAN, um die Dokumentation dort zu erhalten. Es ist absolut nichts Falsches daran, auf CPAN-Module zu verweisen, da sich dort die vollständige Dokumentation befinden soll. In vielen Fällen hatte ich jedoch Probleme, vollständige Arbeitscodebeispiele zu finden.

Ich habe im gesamten Internet nach Beispielen für funktionierenden Code gesucht, die ich herunterladen oder in meine IDE einfügen kann, wie in Ihrem typischen Tutorial-Beispielquellcode "Hello World", aber anhand eines Beispiels, das den Erstellungsprozess mit Unit-Tests und Code demonstriert Abdeckungsanalyse. Hat jemand ein kleines Beispiel für ein komplettes Arbeitsprojekt, das diese Technologien und Prozesse demonstriert?

(Ich habe ein kleines Arbeitsbeispiel und werde meine eigene Frage damit beantworten, aber es gibt wahrscheinlich andere SO-Benutzer, die bessere Beispiele haben als die, die ich mir ausgedacht habe.)

Kurt W. Leucht
quelle

Antworten:

104

Es hat eine Weile gedauert und ich habe auch kleine Schnipsel aus verschiedenen Quellen genommen und zusammengeschmolzen, aber ich glaube, ich habe ein kleines Arbeitsbeispiel, das einem Perl-Neuling den Perl-Erstellungsprozess einschließlich Unit-Tests und Codeabdeckung hinreichend demonstriert Analyse & Berichterstattung. (Ich verwende ActiveState ActivePerl v5.10.0 auf einem Windows XP Pro-PC, Module :: Build , Test :: More , Devel :: Cover )

Beginnen Sie mit einem Verzeichnis für Ihr Perl-Projekt und erstellen Sie dann ein "lib" -Verzeichnis und ein "t" -Verzeichnis unter Ihrem Projektverzeichnis:

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

Erstellen Sie im Verzeichnis "lib" eine Textdatei mit dem Namen "HelloPerlBuildWorld.pm". Diese Datei ist Ihr Perl-Modul, das Sie erstellen und testen werden. Fügen Sie den folgenden Inhalt in diese Datei ein:

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;

Erstellen Sie im Verzeichnis "t" eine Textdatei mit dem Namen "HelloPerlBuildWorld.t". Diese Datei ist Ihr Unit-Test-Skript, das versucht, Ihr Perl-Modul oben vollständig zu testen. Fügen Sie den folgenden Inhalt in diese Datei ein:

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

Erstellen Sie nun in Ihrem Projektverzeichnis der obersten Ebene eine Textdatei mit dem Namen "Build.PL". Diese Datei erstellt Ihre Build-Skripte, die Sie später verwenden werden. Fügen Sie den folgenden Inhalt in diese Datei ein:

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

Das sind alle Dateien, die Sie brauchen. Geben Sie nun in der Befehlszeile im Projektverzeichnis der obersten Ebene den folgenden Befehl ein:

perl Build.PL

Sie sehen etwas Ähnliches wie das Folgende:

Checking prerequisites...
Looks good

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

Jetzt sollten Sie in der Lage sein, Ihre Komponententests mit dem folgenden Befehl auszuführen:

Build test

Und sehen Sie etwas Ähnliches:

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)

Versuchen Sie Folgendes, um Ihre Komponententests mit Codeabdeckungsanalyse durchzuführen:

Build testcover

Und Sie werden etwas in der Reihenfolge sehen:

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.

(Jemand sagt mir bitte, wie ich Cover so konfigurieren soll, dass alle Perl-Bibliotheken außer ignoriert werden, und erstattet mir einfach Bericht über meine einzelne Datei, die ich geschrieben habe. Ich konnte die Cover-Filterung nicht gemäß der CPAN-Dokumentation zum Laufen bringen!)

Wenn Sie jetzt Ihr Verzeichnis der obersten Ebene aktualisieren, wird ein neues Unterverzeichnis mit dem Namen "cover_db" angezeigt. Gehen Sie in dieses Verzeichnis und doppelklicken Sie auf die Datei "Coverage.html", um den Code Coverage-Bericht in Ihrem bevorzugten Webbrowser zu öffnen. Sie erhalten einen schönen farbcodierten Hypertext-Bericht, in dem Sie auf Ihren Dateinamen klicken und detaillierte Statistiken zu Anweisungen, Zweigen, Bedingungen und Unterprogrammabdeckungen für Ihr Perl-Modul direkt im Bericht neben dem eigentlichen Quellcode anzeigen können. Sie können in diesem Bericht sehen, dass wir die Routine "bye ()" überhaupt nicht behandelt haben und dass es auch eine nicht erreichbare Codezeile gibt, die nicht wie erwartet behandelt wurde.

Momentaufnahme des Codeabdeckungsberichts
(Quelle: leucht.com )

Eine weitere Möglichkeit zur Automatisierung dieses Prozesses in Ihrer IDE besteht darin, weitere Dateien vom Typ "Build.PL" zu erstellen, in denen einige der oben beschriebenen Build-Ziele explizit manuell über die Befehlszeile ausgeführt werden. Zum Beispiel verwende ich eine "BuildTest.PL" -Datei mit folgendem Inhalt:

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

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

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

Dann habe ich meine IDE so eingerichtet, dass diese Datei (über "perl BuiltTest.PL") mit einem einzigen Mausklick ausgeführt wird, und mein Unit-Test-Code wird automatisch über die IDE ausgeführt, anstatt dass ich ihn manuell über die Befehlszeile ausführe. Ersetzen Sie den "Versand ('Test')" durch "Versand ('Testcover')" für die automatische Ausführung der Codeabdeckung. Geben Sie "Build-Hilfe" ein, um eine vollständige Liste der Build-Ziele zu erhalten, die unter Module :: Build verfügbar sind.

Kurt W. Leucht
quelle
1
Ihre Idee, einen BuiltTest.PL einzurichten, klingt für mich nicht gut. Warum kannst du nicht einfach ein Skript schreiben, das das tut Build buildund dann Build test?
Leon Timmermans
2
Leon, schlagen Sie ein Perl-Skript vor, das Befehlszeilenaufrufe ausführt? Wenn ja, würde ich lieber keine Befehlszeilenaufrufe tätigen, wenn es eine OO-Möglichkeit gibt, die Aufrufe programmgesteuert durchzuführen, wie in der Beispieldatei BuiltTest.PL.
Kurt W. Leucht
1
Das ist nicht nötig, siehe meine eigene Antwort
Leon Timmermans
2
Module :: Build ist nichts für CPAN. Sie können weiterhin alle Funktionen der verschiedenen CPAN-Tools abrufen, auch wenn diese nicht in CPAN enthalten sind. Sie können es weiterhin mit demselben Prozess erstellen, testen, verteilen und installieren, obwohl es sich um ein privates Modul handelt.
Brian D Foy
4
Um Ergebnisse in Devel :: Cover zu filtern, füge ich Optionen hinzu $ENV{HARNESS_PERL_SWITCHES}. Beispiel: -MDevel::Cover=+ignore,.t$,+inc,/app/lib,-select,MyModule.pmWo /app/libbefindet sich die anwendungsprivate Bibliothek und MyModule.pmdas zu testende Modul?
Michael Carman
14

Als Antwort auf Kurt würde ich diese Alternative zu seinem BuiltTest.PL-Skript vorschlagen.

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

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

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

Es verwendet die von Build.PL erstellte Datenbank erneut (und geht daher davon aus, dass diese bereits ausgeführt wurde).

Leon Timmermans
quelle
Perfekt! Danke, Leon. Ich wusste, dass etwas mit meinem Beispiel nicht stimmt, aber ich bin immer noch neu in diesem Perl-Build-Zeug! :-)
Kurt W. Leucht
12

Ich beschreibe dies sowohl in Intermediate Perl als auch in Mastering Perl . Kurt hat jedoch eine schöne Zusammenfassung gegeben.

All dies kombiniere ich jedoch mit Module :: Release zu einem Release-Skript . Ich tippe einen Befehl und alles passiert.

brian d foy
quelle
12

Das fantastisch hilfreiche module-starterProjekt generiert ein benutzerfreundliches Skeleton-Projekt, das die Installation von Modulen, die Erstellung von Dokumentationen und ein gutes Layout für Moduldateien sowie - glaube ich - die Unterstützung der Codeabdeckung übernimmt. Es ist IMO ein großartiger Start für jedes Unternehmen im Zusammenhang mit Perl-Modulen.

Außerdem ist die Verwendung von CPAN-bezogenen Tools wie Module::Build- auch für Module, die wahrscheinlich nie öffentlich veröffentlicht werden - eine sehr gute Idee .

Gaurav
quelle
7

(Offenlegung: Ich bin der Autor)

Sobald Sie alles wie oben beschrieben sortiert haben, können Sie den nächsten Schritt ausführen und Devel :: CoverX :: Covered verwenden, um z

  • Listen Sie bei einer gegebenen Quelldatei die Testdateien auf, die diese Quelldatei abdecken. Dies kann auf Datei-, Unterroutinen- und Zeilenebene erfolgen.
  • Listen Sie anhand einer Testdatei die Quelldateien und Subs auf, die von dieser Testdatei abgedeckt werden.
  • Berichten Sie bei einer gegebenen Quelldatei effizient über die Abdeckungsdetails pro Zeile oder Unterabschnitt.

In der Übersicht finden Sie konkrete Beispiele für Befehlszeilen.

In Devel :: PerlySense gibt es Emacs-Unterstützung, um die Abdeckungsinformationen im Quellcode-Puffer anzuzeigen ( Screenshot ) und zu / von abdeckenden Testdateien zu navigieren.

jplindstrom
quelle