Kompilacja Perla, testy jednostkowe, pokrycie kodu: kompletny przykład roboczy

86

Większość odpowiedzi Stackoverflow, które znalazłem w odniesieniu do procesu budowania Perla, testów jednostkowych i pokrycia kodu po prostu kieruje mnie do CPAN w celu uzyskania tam dokumentacji. Nie ma absolutnie nic złego w wskazywaniu na moduły CPAN, ponieważ tam powinna znajdować się pełna dokumentacja. Jednak w wielu przypadkach miałem problemy ze znalezieniem kompletnych przykładów kodu działającego.

Szukałem w całym Internecie działających przykładów kodu, które mogę pobrać lub wkleić do mojego IDE, na przykład w typowym samouczku „Hello World”, przykładowy kod źródłowy, ale przykład, który demonstruje proces kompilacji za pomocą testów jednostkowych i kodu analiza pokrycia. Czy ktoś ma mały przykład kompletnego, działającego projektu, który demonstruje te technologie i procesy?

(Mam mały przykład roboczy i odpowiem nim na własne pytanie, ale prawdopodobnie są inni użytkownicy SO, którzy mają lepsze przykłady niż te, które wymyśliłem).

Kurt W. Leucht
źródło

Odpowiedzi:

105

Zajęło mi to trochę czasu, a także zebranie małych fragmentów z wielu różnych źródeł i połączenie ich razem, ale myślę, że mam mały przykład roboczy, który wystarczająco demonstruje nowicjuszowi Perla proces kompilacji Perla, w tym testy jednostkowe i pokrycie kodu analizy i raportowanie. (Używam ActiveState ActivePerl v5.10.0 na komputerze z systemem Windows XP Pro, Module :: Build , Test :: More , Devel :: Cover )

Zacznij od katalogu dla projektu Perl, a następnie utwórz katalog „lib” i katalog „t” w katalogu projektu:

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

W katalogu „lib” utwórz plik tekstowy o nazwie „HelloPerlBuildWorld.pm”. Ten plik jest Twoim modułem Perla, który będziesz budować i testować. Wklej następującą zawartość do tego pliku:

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;

W katalogu „t” utwórz plik tekstowy o nazwie „HelloPerlBuildWorld.t”. Ten plik to skrypt do testów jednostkowych, który spróbuje w pełni przetestować powyższy moduł Perla. Wklej następującą zawartość do tego pliku:

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

Teraz utwórz kopię zapasową w katalogu projektu najwyższego poziomu, utwórz plik tekstowy o nazwie „Build.PL”. Ten plik utworzy skrypty budowania, których będziesz używać później. Wklej następującą zawartość do tego pliku:

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

To wszystkie potrzebne pliki. Teraz z wiersza poleceń w katalogu projektu najwyższego poziomu wpisz następujące polecenie:

perl Build.PL

Zobaczysz coś podobnego do następującego:

Checking prerequisites...
Looks good

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

Teraz powinieneś być w stanie uruchomić testy jednostkowe za pomocą następującego polecenia:

Build test

I zobacz coś podobnego do tego:

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)

Aby uruchomić testy jednostkowe z analizą pokrycia kodu, spróbuj tego:

Build testcover

I zobaczysz coś w kolejności:

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.

(Niech ktoś mi powie, jak skonfigurować Cover, aby ignorował wszystkie biblioteki Perla z wyjątkiem i po prostu zgłaszał mi mój pojedynczy plik, który napisałem. Nie mogłem sprawić, by filtrowanie Cover działało zgodnie z dokumentacją CPAN!)

Teraz, jeśli odświeżysz katalog najwyższego poziomu, zobaczysz nowy podkatalog o nazwie „cover_db”. Przejdź do tego katalogu i kliknij dwukrotnie plik „zasięg.html”, aby otworzyć raport pokrycia kodu w ulubionej przeglądarce internetowej. Daje ci ładny, zakodowany kolorami raport hipertekstowy, w którym możesz kliknąć nazwę pliku i zobaczyć szczegółowe zestawienie, gałąź, stan, statystyki pokrycia podprogramów dla twojego modułu Perla bezpośrednio w raporcie obok faktycznego kodu źródłowego. W tym raporcie widać, że w ogóle nie omówiliśmy procedury „bye ()”, a także istnieje wiersz kodu, który jest nieosiągalny i nie został uwzględniony zgodnie z oczekiwaniami.

migawka raportu pokrycia kodu
(źródło: leucht.com )

Jeszcze jedną rzeczą, którą możesz zrobić, aby pomóc zautomatyzować ten proces w swoim IDE, jest utworzenie większej liczby plików typu „Build.PL”, które jawnie wykonują niektóre z celów kompilacji, które wykonaliśmy powyżej ręcznie z wiersza poleceń. Na przykład używam pliku „BuildTest.PL” o następującej treści:

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

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

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

Następnie skonfigurowałem moje IDE tak, aby wykonywało ten plik (przez „perl BuiltTest.PL”) jednym kliknięciem myszy i automatycznie uruchamia mój kod testu jednostkowego z IDE zamiast robić to ręcznie z wiersza poleceń. Zastąp „wysyłka ('test')” słowem „wysyłka ('testcover')”, aby wykonać automatyczne pokrycie kodu. Wpisz „Pomoc dotycząca kompilacji”, aby uzyskać pełną listę celów kompilacji, które są dostępne w module :: Build.

Kurt W. Leucht
źródło
1
Twój pomysł na utworzenie BuiltTest.PL nie brzmi dla mnie dobrze. Dlaczego nie można po prostu napisać skrypt, który robi Build build, a potem Build test?
Leon Timmermans
2
Leon, czy sugerujesz skrypt Perla, który wykonuje wywołania wiersza poleceń? Jeśli tak, wolałbym nie wykonywać wywołań wiersza poleceń, jeśli istnieje sposób OO na programowe wykonywanie wywołań, jak w przykładowym pliku BuiltTest.PL.
Kurt W. Leucht
1
To nie jest konieczne, zobacz moją własną odpowiedź
Leon Timmermans
2
Module :: Build nie jest przeznaczone dla CPAN. Nadal możesz uzyskać wszystkie funkcje z różnych narzędzi CPAN, nawet jeśli nie są one dostępne w CPAN. Nadal można go budować, testować, rozpowszechniać i instalować za pomocą tego samego procesu, nawet jeśli jest to moduł prywatny.
brian d foy
4
Aby filtrować wyniki w Devel :: Cover, dodaję opcje do $ENV{HARNESS_PERL_SWITCHES}. Na przykład: -MDevel::Cover=+ignore,.t$,+inc,/app/lib,-select,MyModule.pmgdzie /app/libjest prywatną biblioteką aplikacji i MyModule.pmjest testowanym modułem.
Michael Carman,
14

W odpowiedzi na Kurta zaproponowałbym tę alternatywę dla jego skryptu BuiltTest.PL.

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

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

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

Ponownie wykorzystuje bazę danych zbudowaną przez Build.PL (i tym samym zakłada, że ​​została już uruchomiona).

Leon Timmermans
źródło
Idealny! Dzięki, Leon. Wiedziałem, że coś jest nie tak z moim przykładem, ale sam wciąż jestem nowy w tej kompilacji Perla! :-)
Kurt W. Leucht
12

Fantastycznie pomocny module-startergeneruje łatwy w użyciu szkielet projektu, który zajmuje się instalacją modułów, tworzeniem dokumentacji i dobrym układem plików modułów do zamieszkania oraz - jak sądzę - obsługą pokrycia kodu. To IMO świetny początek dla każdego przedsięwzięcia związanego z modułami Perla.

Ponadto: używanie narzędzi związanych z CPAN, takich jak Module::Build- nawet w przypadku modułów, które prawdopodobnie nigdy nie zostaną opublikowane publicznie - jest bardzo dobrym pomysłem .

Gaurav
źródło
7

(ujawnienie: jestem autorem)

Po posortowaniu wszystkiego, jak opisano powyżej, możesz zrobić następny krok i użyć Devel :: CoverX :: Covered np

  • Mając plik źródłowy, wypisz pliki testowe, które zapewniają pokrycie tego pliku źródłowego. Można to zrobić na poziomie pliku, procedury podrzędnej i wiersza.
  • Mając plik testowy, wypisz pliki źródłowe i subskrypcje objęte tym plikiem testowym.
  • Mając plik źródłowy, wydaj efektywne raportowanie szczegółów pokrycia według wiersza lub podrzędnego.

Zobacz streszczenie konkretnych przykładów wiersza poleceń.

W Devel :: PerlySense dostępna jest obsługa Emacs do wyświetlania informacji o pokryciu w buforze kodu źródłowego ( zrzut ekranu ) oraz do przechodzenia do / z obejmujących pliki testowe.

jplindstrom
źródło