81

La mayoría de las respuestas de Stackoverflow que he encontrado con respecto al proceso de compilación de Perl y las pruebas unitarias y la cobertura de código simplemente me señalan CPAN para la documentación allí. No hay absolutamente nada de malo en señalar los módulos de CPAN porque ahí es donde se supone que reside la documentación completa. Sin embargo, he tenido problemas para encontrar ejemplos de códigos de trabajo completos en muchos casos.Perl build, unit testing, code coverage: Un ejemplo de trabajo completo

He estado buscando en todo el Internet ejemplos de códigos de trabajo reales que puedo descargar o pegar en mi IDE, como el tutorial típico "Hello World", pero de un ejemplo que demuestra el proceso de compilación con pruebas unitarias y análisis de cobertura de código. ¿Alguien tiene un pequeño ejemplo de un proyecto de trabajo completo que demuestre estas tecnologías y procesos?

(tengo un pequeño ejemplo de trabajo y voy a responder a mi propia pregunta con él, pero probablemente hay otros para que los usuarios que tienen mejores ejemplos que los que se me ocurrió.)

Respuesta

101

Tardó un rato y también me llevó a tomar pequeños fragmentos de diferentes fuentes y fundirlos, pero creo que tengo un pequeño ejemplo de trabajo que demuestra suficientemente a un novato de Perl el proceso de compilación de Perl, incluidas pruebas unitarias y análisis de cobertura de código & reportando (Estoy usando ActiveState ActivePerl v5.10.0 en un PC con Windows XP Pro, Module::Build, Test::More, Devel::Cover)

comenzar con un directorio para su proyecto Perl y luego crear un directorio "lib" y un directorio "t" bajo el directorio del proyecto:

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

En el directorio "lib", crear un archivo de texto llamado "HelloPerlBuildWorld.pm". Este archivo es su módulo Perl que construirá y probará. Pegue el siguiente contenido a este archivo:

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; 

En el directorio "t", cree un archivo de texto llamado "HelloPerlBuildWorld.t". Este archivo es el script de prueba de su unidad que intentará probar completamente su módulo Perl anterior. Pegue el siguiente contenido a este archivo:

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

Ahora, de vuelta en su directorio principal del proyecto de nivel, crear un archivo de texto llamado "Build.PL". Este archivo creará sus scripts de compilación que usará más adelante. Pegue el siguiente contenido en este archivo:

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

Esos son todos los archivos que necesita.Ahora, desde la línea de comandos en el directorio superior de proyectos de nivel, escriba el siguiente comando:

perl Build.PL 

Usted verá algo similar a lo siguiente:

Checking prerequisites... 
Looks good 

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

Ahora usted debería ser capaz de ejecutar las pruebas unitarias con el siguiente comando:

Build test 

y ver algo similar a esto:

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 ejecutar las pruebas unitarias con el análisis de la cobertura de código, intente esto:

Build testcover 

Y verá algo del orden de lo siguiente:

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. 

(Alguien por favor me dicen cómo para configurar Cover para ignorar todas las bibliotecas de Perl excepto y solo informarme en mi único archivo que escribí. ¡No pude hacer que el filtrado de portadas funcionara de acuerdo con la documentación de CPAN!)

Ahora, si actualiza su directorio de nivel superior, puede ver un nuevo subdirectorio llamado "cover_db". Vaya a ese directorio y haga doble clic en el archivo "coverage.html" para abrir el informe de cobertura de código en su navegador web favorito. Le da un buen informe de hipertexto codificado por color donde puede hacer clic en su nombre de archivo y ver las estadísticas detalladas de estado, sucursal, estado y cobertura de subrutina para su módulo Perl en el informe al lado del código fuente actual. Puede ver en este informe que no cubrimos la rutina "bye()" y también hay una línea de código que no se pudo alcanzar y que no estaba cubierta como esperábamos.

snapshot of code coverage report http://www.leucht.com/images/CodeCoverageExample.jpg

Una cosa más que puede hacer para ayudar a automatizar este proceso en su IDE es hacer algunos más archivos de tipo "Build.PL" que realizan de forma explícita algunos de los tipos de generación que hicimos anteriormente manualmente desde el línea de comando. Por ejemplo, utilizo un archivo "BuildTest.PL" con el siguiente contenido:

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

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

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

Entonces creé mi IDE para ejecutar este archivo (a través de "perl BuiltTest.PL") con un solo clic del ratón y automáticamente ejecuta mi código de prueba unitaria desde el IDE en lugar de hacerlo manualmente desde la línea de comando. Reemplace el "despacho ('prueba')" por "despacho ('prueba')" para la ejecución automática de la cobertura del código. Escriba "Ayuda de compilación" para obtener una lista completa de los objetivos de compilación que están disponibles en Module :: Build.

+1

Tu idea para configurar BuiltTest.PL no me parece bien. ¿Por qué no puedes escribir un script que hace 'Build build' y luego' Build test'? –

+2

Leon, ¿estás sugiriendo una secuencia de comandos perl que realice llamadas de línea de comando? Si es así, prefiero no realizar llamadas de línea de comandos si hay una forma OO de realizar las llamadas de forma programática como en el archivo BuiltTest.PL de ejemplo. –

+1

Eso no es necesario, ver mi propia respuesta –

14

En respuesta a Kurt, propongo esta alternativa a su 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'); 

Reutiliza la compilación de la base de datos por Build.PL (y asume que ya se ejecutó).

+0

¡Perfecto! Gracias, Leon. ¡Sabía que algo andaba mal con mi ejemplo, pero todavía soy nuevo en esta construcción perl! :-) –

12

El increíblemente útil module-starter genera un proyecto de carcasa y fácil de usar que se encarga de la instalación del módulo, creación de documentación y una buena distribución de archivos de módulo para vivir, y - lo que - Soporte de cobertura de código. Es IMO un gran comienzo para cualquier esfuerzo relacionado con el módulo Perl.

También: utilizando herramientas relacionadas con CPAN como Module::Build - incluso para módulos que probablemente nunca se publiquen públicamente - is a very good idea.

7

(divulgación: Soy el autor)

Una vez que haya todo resuelto como se describió anteriormente, se puede dar el siguiente paso y utilizar Devel::CoverX::Covered a, por ejemplo

  • Dado un archivo fuente, enumere los archivos de prueba que proporcionan cobertura a ese archivo fuente. Esto se puede hacer en un archivo, sub rutina y nivel de fila.
  • Dado un archivo de prueba, enumere los archivos fuente y los subs cubiertos por ese archivo de prueba.
  • Dado un archivo fuente, informe de manera eficiente sobre los detalles de la cobertura por fila o sub.

Consulte el synopsis para obtener ejemplos concretos de líneas de comando.

En Devel::PerlySense hay compatibilidad con Emacs para mostrar la información de cobertura en el búfer de código fuente (screen shot), y para navegar hacia/desde la cobertura de archivos de prueba.