Compilação de Perl, teste de unidade, cobertura de código: um exemplo de trabalho completo

86

A maioria das respostas do Stackoverflow que encontrei em relação ao processo de construção do Perl e aos testes de unidade e cobertura de código simplesmente me apontam para o CPAN para a documentação lá. Não há absolutamente nada de errado em apontar para módulos CPAN porque é onde toda a documentação deve residir. No entanto, tive problemas para encontrar exemplos de código de trabalho completos em muitos casos.

Eu tenho pesquisado em toda a Internet por exemplos de código de trabalho reais que eu possa baixar ou colar em meu IDE, como seu exemplo de código-fonte típico de tutorial "Hello World", mas de um exemplo que demonstra o processo de construção com teste de unidade e código análise de cobertura. Alguém tem um pequeno exemplo de um projeto de trabalho completo que demonstra essas tecnologias e processos?

(Eu tenho um pequeno exemplo de trabalho e responderei minha própria pergunta com ele, mas provavelmente há outros usuários de SO que têm exemplos melhores do que aqueles que eu criei.)

Kurt W. Leucht
fonte

Respostas:

105

Levei um tempo e também peguei pequenos trechos de várias fontes diferentes e os fundi, mas acho que tenho um pequeno exemplo de trabalho que demonstra suficientemente para um novato em Perl o processo de construção do Perl, incluindo testes de unidade e cobertura de código análise e relatórios. (Estou usando ActiveState ActivePerl v5.10.0 em um PC com Windows XP Pro, Módulo :: Construir , Teste :: Mais , Devel :: Capa )

Comece com um diretório para seu projeto Perl e, em seguida, crie um diretório "lib" e um diretório "t" no diretório do projeto:

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

No diretório "lib", crie um arquivo de texto denominado "HelloPerlBuildWorld.pm". Este arquivo é o seu módulo Perl que você irá construir e testar. Cole o seguinte conteúdo neste arquivo:

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;

No diretório "t", crie um arquivo de texto denominado "HelloPerlBuildWorld.t". Este arquivo é o seu script de teste de unidade que tentará testar totalmente o módulo Perl acima. Cole o seguinte conteúdo neste arquivo:

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

Agora faça backup no diretório do projeto de nível superior, crie um arquivo de texto chamado "Build.PL". Este arquivo criará seus scripts de construção que você usará posteriormente. Cole o seguinte conteúdo neste arquivo:

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

Esses são todos os arquivos de que você precisa. Agora, na linha de comando no diretório do projeto de nível superior, digite o seguinte comando:

perl Build.PL

Você verá algo semelhante ao seguinte:

Checking prerequisites...
Looks good

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

Agora você deve ser capaz de executar seus testes de unidade com o seguinte comando:

Build test

E veja algo semelhante a isto:

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)

Para executar seus testes de unidade com análise de cobertura de código, tente isto:

Build testcover

E você verá algo nesta ordem:

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.

(Alguém, por favor, me diga como configurar o Cover para ignorar todas as bibliotecas Perl, exceto e apenas relatar para mim o meu único arquivo que escrevi. Não consegui fazer a filtragem do Cover funcionar de acordo com a documentação do CPAN!)

Agora, se você atualizar seu diretório de nível superior, poderá ver um novo subdiretório chamado "cover_db". Vá para esse diretório e clique duas vezes no arquivo "cobertura.html" para abrir o relatório de cobertura de código em seu navegador da web favorito. Ele fornece um relatório de hipertexto com código de cores agradável, onde você pode clicar no nome do arquivo e ver estatísticas detalhadas de declaração, ramificação, condição e cobertura de subrotina para o seu módulo Perl ali mesmo no relatório ao lado do código-fonte real. Você pode ver neste relatório que não cobrimos a rotina "bye ()" e também há uma linha de código que está inacessível que não foi abordada como esperávamos.

instantâneo do relatório de cobertura de código
(fonte: leucht.com )

Mais uma coisa que você pode fazer para ajudar a automatizar esse processo em seu IDE é criar mais alguns arquivos do tipo "Build.PL" que executam explicitamente alguns dos destinos de construção que fizemos acima manualmente a partir da linha de comando. Por exemplo, eu uso um arquivo "BuildTest.PL" com o seguinte conteúdo:

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

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

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

Então eu configuro meu IDE para executar este arquivo (via "perl BuiltTest.PL") com um único clique do mouse e ele executa automaticamente meu código de teste de unidade do IDE em vez de eu fazê-lo manualmente a partir da linha de comando. Substitua o "dispatch ('test')" por "dispatch ('testcover')" para execução de cobertura de código automatizada. Digite "Build help" para obter uma lista completa de destinos de build disponíveis em Module :: Build.

Kurt W. Leucht
fonte
1
Sua ideia de configurar um BuiltTest.PL não me parece boa. Por que você não pode simplesmente escrever um script que o faça Build builde então Build test?
Leon Timmermans
2
Leon, você está sugerindo um script perl que faz chamadas de linha de comando? Em caso afirmativo, prefiro não fazer chamadas de linha de comando se houver uma maneira OO de fazer as chamadas programaticamente como no arquivo BuiltTest.PL de exemplo.
Kurt W. Leucht
1
Isso não é necessário, veja minha própria resposta
Leon Timmermans
2
Módulo :: Construir simplesmente não é para CPAN. Você ainda pode obter todos os recursos das várias ferramentas CPAN, mesmo que não esteja no CPAN. Você ainda pode construir, testar, distribuir e instalar com o mesmo processo, embora seja um módulo privado.
brian d foy
4
Para filtrar resultados em Devel :: Cover, adiciono opções a $ENV{HARNESS_PERL_SWITCHES}. Por exemplo: -MDevel::Cover=+ignore,.t$,+inc,/app/lib,-select,MyModule.pmonde /app/libestá a biblioteca privada do aplicativo e MyModule.pmé o módulo em teste.
Michael Carman
14

Em resposta a Kurt, eu proporia esta alternativa para seu 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');

Ele reutiliza o banco de dados construído por Build.PL (e, portanto, assume que já foi executado).

Leon Timmermans
fonte
Perfeito! Obrigado, Leon. Eu sabia que algo estava errado com meu exemplo, mas eu ainda sou novo nesse tipo de construção de perl! :-)
Kurt W. Leucht
12

O fantasticamente útil module-startergera um projeto de esqueleto fácil de usar que lida com a instalação do módulo, criação de documentação e um bom layout para os arquivos do módulo morarem e - eu acho - suporte para cobertura de código. É IMO um ótimo começo para qualquer empreendimento relacionado ao módulo Perl.

Além disso: usar ferramentas relacionadas ao CPAN como Module::Build- mesmo para módulos que provavelmente nunca serão lançados publicamente - é uma idéia muito boa .

Gaurav
fonte
7

(divulgação: eu sou o autor)

Depois de ter tudo classificado conforme descrito acima, você pode dar o próximo passo e usar Devel :: CoverX :: Covered para, por exemplo,

  • Dado um arquivo de origem, liste os arquivos de teste que fornecem cobertura para esse arquivo de origem. Isso pode ser feito em um arquivo, sub-rotina e nível de linha.
  • Dado um arquivo de teste, liste os arquivos de origem e subs abrangidos por esse arquivo de teste.
  • Dado um arquivo de origem, reporte com eficiência os detalhes de cobertura por linha ou sub.

Veja a sinopse para exemplos concretos de linha de comando.

Em Devel :: PerlySense, há suporte Emacs para exibir as informações de cobertura no buffer de código-fonte ( captura de tela ) e para navegar de / para os arquivos de teste de cobertura.

jplindstrom
fonte