Compilación de Perl, pruebas unitarias, cobertura de código: un ejemplo de trabajo completo

86

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 del código simplemente me indican a CPAN para obtener la documentación allí. No hay absolutamente nada de malo en apuntar a los módulos CPAN porque ahí es donde se supone que reside la documentación completa. Sin embargo, he tenido problemas para encontrar ejemplos completos de código de trabajo en muchos casos.

He estado buscando por todo Internet ejemplos de código de trabajo real que pueda descargar o pegar en mi IDE, como el típico código fuente de ejemplo del tutorial "Hello World", pero de un ejemplo que demuestra el proceso de compilación con pruebas unitarias y código análisis de cobertura. ¿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 responderé mi propia pregunta con él, pero probablemente hay otros usuarios de SO que tienen mejores ejemplos que los que se me ocurrieron).

Kurt W. Leucht
fuente

Respuestas:

105

Me tomó un tiempo y también me tomó tomar pequeños fragmentos de varias fuentes diferentes y fusionarlos, 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 las pruebas unitarias y la cobertura de código análisis e informes. (Estoy usando ActiveState ActivePerl v5.10.0 en una PC con Windows XP Pro, Module :: Build , Test :: More , Devel :: Cover )

Comience con un directorio para su proyecto Perl y luego cree un directorio "lib" y un directorio "t" en el directorio de su proyecto:

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

En el directorio "lib", cree un archivo de texto llamado "HelloPerlBuildWorld.pm". Este archivo es su módulo Perl que estará construyendo y probando. Pegue el siguiente contenido en 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 su secuencia de comandos de prueba unitaria que intentará probar completamente su módulo Perl anterior. Pegue el siguiente contenido en 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 haga una copia de seguridad en el directorio de su proyecto de nivel superior, cree 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 comando en el directorio del proyecto de nivel superior, escriba el siguiente comando:

perl Build.PL

Verá algo similar a lo siguiente:

Checking prerequisites...
Looks good

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

Ahora debería poder ejecutar sus pruebas unitarias con el siguiente comando:

Build test

Y vea 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 sus pruebas unitarias con análisis de cobertura de código, intente esto:

Build testcover

Y verá algo en el orden de esto:

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, dígame cómo configurar Cover para ignorar todas las bibliotecas de Perl excepto y solo informarme sobre mi único archivo que escribí. ¡No pude hacer que el filtrado de Cover funcione 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 "coberturas.html" para abrir el informe de cobertura del código en su navegador web favorito. Le brinda un buen informe de hipertexto codificado por colores donde puede hacer clic en el nombre de su archivo y ver estadísticas detalladas de cobertura, rama, condición y subrutina para su módulo Perl allí mismo en el informe junto al código fuente real. Puede ver en este informe que no cubrimos la rutina "bye ()" en absoluto y también hay una línea de código inalcanzable que no se cubrió como esperábamos.

instantánea del informe de cobertura de código
(fuente: leucht.com )

Una cosa más que puede hacer para ayudar a automatizar este proceso en su IDE es crear más archivos de tipo "Build.PL" que realicen explícitamente algunos de los objetivos de compilación que hicimos anteriormente de forma manual desde la línea de comandos. Por ejemplo, uso 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');

Luego configuré mi IDE para ejecutar este archivo (a través de "perl BuiltTest.PL") con un solo clic del mouse y automáticamente ejecuta mi código de prueba de unidad desde el IDE en lugar de hacerlo yo manualmente desde la línea de comando. Reemplace "dispatch ('test')" por "dispatch ('testcover')" para la ejecución automatizada 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.

Kurt W. Leucht
fuente
1
Tu idea de configurar un BuiltTest.PL no me suena bien. ¿Por qué no puedes escribir un guión que lo haga Build buildy luego Build test?
Leon Timmermans
2
Leon, ¿estás sugiriendo un script en Perl que haga llamadas a la línea de comandos? Si es así, prefiero no hacer llamadas a la línea de comandos si hay una forma OO de hacer las llamadas programáticamente como en el archivo de ejemplo BuiltTest.PL.
Kurt W. Leucht
1
Eso no es necesario, vea mi propia respuesta
Leon Timmermans
2
Module :: Build simplemente no es para CPAN. Aún puede obtener todas las funciones de las diversas herramientas de CPAN incluso si no está en CPAN. Aún puede compilarlo, probarlo, distribuirlo e instalarlo con el mismo proceso aunque sea un módulo privado.
Brian D Foy
4
Para filtrar los resultados en Devel :: Cover, agrego opciones a $ENV{HARNESS_PERL_SWITCHES}. Por ejemplo: -MDevel::Cover=+ignore,.t$,+inc,/app/lib,-select,MyModule.pmdónde /app/libestá la biblioteca privada de la aplicación y MyModule.pmel módulo que se está probando.
Michael Carman
14

En respuesta a Kurt, propondría 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 base de datos construida por Build.PL (y por lo tanto asume que ya se ejecutó).

Leon Timmermans
fuente
¡Perfecto! Gracias, Leon. Sabía que algo andaba mal con mi ejemplo, ¡pero todavía soy nuevo en este material de perl build! :-)
Kurt W. Leucht
12

Lo fantásticamente útil module-startergenera un proyecto esqueleto fácil de usar que maneja la instalación de módulos, la creación de documentación y un buen diseño para que los archivos de los módulos vivan y, creo , soporte de cobertura de código. En mi opinión, es un gran comienzo para cualquier esfuerzo relacionado con el módulo Perl.

Además: usar herramientas relacionadas con CPAN como Module::Build, incluso para módulos que probablemente nunca se lanzarán públicamente, es una muy buena idea .

Gaurav
fuente
7

(divulgación: soy el autor)

Una vez que tenga todo ordenado como se describe anteriormente, puede dar el siguiente paso y usar Devel :: CoverX :: Covered para, por ejemplo,

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

Consulte la sinopsis para ver ejemplos concretos de líneas de comandos.

En Devel :: PerlySense hay soporte de Emacs para mostrar la información de cobertura en el búfer del código fuente ( captura de pantalla ) y para navegar hacia / desde los archivos de prueba de cobertura.

jplindstrom
fuente