Сборка Perl, модульное тестирование, покрытие кода: полный рабочий пример

86

Большинство ответов Stackoverflow, которые я нашел в отношении процесса сборки Perl, модульного тестирования и покрытия кода, просто указывают мне на CPAN за документацией. Нет ничего плохого в том, чтобы указывать на модули CPAN, потому что там должна находиться вся документация. Однако во многих случаях у меня были проблемы с поиском полных примеров рабочего кода.

Я искал по всему Интернету реальные образцы рабочего кода, которые я могу загрузить или вставить в свою IDE, например исходный код вашего типичного учебника «Hello World», но пример, демонстрирующий процесс сборки с модульным тестированием и кодом. анализ покрытия. Есть ли у кого-нибудь небольшой пример полного рабочего проекта, демонстрирующего эти технологии и процессы?

(У меня есть небольшой рабочий пример, и я отвечу им на свой вопрос, но, вероятно, есть другие пользователи SO, у которых есть лучшие примеры, чем те, которые я придумал.)

Курт В. Лойхт
источник

Ответы:

105

Это заняло у меня некоторое время, и мне также потребовалось взять небольшие фрагменты из ряда разных источников и объединить их вместе, но я думаю, что у меня есть небольшой рабочий пример, который достаточно демонстрирует новичку Perl процесс сборки Perl, включая модульное тестирование и покрытие кода. анализ и отчетность. (Я использую ActiveState ActivePerl v5.10.0 на ПК с Windows XP Pro, Module :: Build , Test :: More , Devel :: Cover )

Начните с каталога для вашего проекта Perl, а затем создайте каталог «lib» и каталог «t» в каталоге проекта:

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

В каталоге «lib» создайте текстовый файл с именем «HelloPerlBuildWorld.pm». Этот файл является вашим модулем Perl, который вы будете создавать и тестировать. Вставьте в этот файл следующее содержимое:

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;

В каталоге «t» создайте текстовый файл с именем «HelloPerlBuildWorld.t». Этот файл является вашим сценарием модульного тестирования, который попытается полностью протестировать ваш модуль Perl, указанный выше. Вставьте в этот файл следующее содержимое:

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

Теперь сделайте резервную копию в каталоге проекта верхнего уровня, создайте текстовый файл с именем «Build.PL». Этот файл создаст ваши сценарии сборки, которые вы будете использовать позже. Вставьте в этот файл следующее содержимое:

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_addy@goes.here>',
    build_requires => {
        'Test::More' => '0.10',
    },
);

$builder->create_build_script();

Это все файлы, которые вам нужны. Теперь из командной строки в каталоге проекта верхнего уровня введите следующую команду:

perl Build.PL

Вы увидите нечто похожее на следующее:

Checking prerequisites...
Looks good

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

Теперь вы сможете запускать свои модульные тесты с помощью следующей команды:

Build test

И посмотрите что-то похожее на это:

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)

Чтобы запустить модульные тесты с анализом покрытия кода, попробуйте следующее:

Build testcover

И вы увидите что-то вроде этого:

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.

(Кто-нибудь, пожалуйста, скажите мне, как настроить Cover, чтобы игнорировать все библиотеки Perl, кроме и просто сообщать мне о моем единственном файле, который я написал. Мне не удалось заставить фильтрацию Cover работать в соответствии с документацией CPAN!)

Теперь, если вы обновите каталог верхнего уровня, вы увидите новый подкаталог с именем «cover_db». Зайдите в этот каталог и дважды щелкните файл «extension.html», чтобы открыть отчет о покрытии кода в вашем любимом веб-браузере. Он дает вам красивый гипертекстовый отчет с цветовой кодировкой, в котором вы можете щелкнуть имя своего файла и просмотреть подробную статистику охвата операторов, ветвей, условий и подпрограмм для вашего модуля Perl прямо в отчете рядом с фактическим исходным кодом. Вы можете видеть в этом отчете, что мы вообще не рассмотрели процедуру «bye ()», а также есть строка кода, которая недоступна, но не была обработана, как мы ожидали.

снимок отчета о покрытии кода
(источник: leucht.com )

Еще одна вещь, которую вы можете сделать для автоматизации этого процесса в вашей среде IDE, - это создать еще несколько файлов типа «Build.PL», которые явно выполняют некоторые из целей сборки, которые мы сделали выше, вручную из командной строки. Например, я использую файл «BuildTest.PL» со следующим содержимым:

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

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

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

Затем я настраиваю свою среду IDE для выполнения этого файла (через "perl BuiltTest.PL") одним щелчком мыши, и он автоматически запускает мой код модульного теста из среды IDE, вместо того, чтобы делать это вручную из командной строки. Замените «dispatch ('test')» на «dispatch ('testcover')» для автоматического выполнения покрытия кода. Введите «Справка по сборке», чтобы получить полный список целей сборки, доступных из Module :: Build.

Курт В. Лойхт
источник
1
Ваша идея настроить BuiltTest.PL мне не нравится. Почему вы не можете просто написать сценарий, который делает, Build buildа потом Build test?
Леон Тиммерманс,
2
Леон, вы предлагаете сценарий Perl, который выполняет вызовы командной строки? Если это так, я бы предпочел не выполнять вызовы командной строки, если существует объектно-ориентированный способ выполнения вызовов программно, как в примере файла BuiltTest.PL.
Kurt W. Leucht
1
В этом нет необходимости, см. Мой собственный ответ
Леон Тиммерманс
2
Module :: Build не подходит для CPAN. Вы по-прежнему можете получить все функции различных инструментов CPAN, даже если они не находятся на CPAN. Вы по-прежнему можете создавать, тестировать, распространять и устанавливать его с помощью того же процесса, даже если это частный модуль.
Брайан д Фой,
4
Для фильтрации результатов в Devel :: Cover я добавляю параметры в $ENV{HARNESS_PERL_SWITCHES}. Например: -MDevel::Cover=+ignore,.t$,+inc,/app/lib,-select,MyModule.pmгде /app/lib- частная библиотека приложения, а MyModule.pm- тестируемый модуль.
Майкл Карман
14

В ответ Курту я бы предложил эту альтернативу его скрипту BuiltTest.PL.

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

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

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

Он повторно использует сборку базы данных Build.PL (и, следовательно, предполагает, что она уже была запущена).

Леон Тиммерманс
источник
Отлично! Спасибо, Леон. Я знал, что с моим примером что-то не так, но я сам все еще новичок в этой сборке на Perl! :-)
Kurt W. Leucht
12

Фантастически полезный module-starterсоздает простой в использовании каркасный проект, который обрабатывает установку модуля, создание документации и хороший макет для файлов модулей, в котором они будут жить, и, я думаю, поддержку покрытия кода. Это IMO - отличное начало для любого проекта, связанного с Perl-модулями.

Также: использование связанных с CPAN инструментов, таких как Module::Build- даже для модулей, которые, вероятно, никогда не будут выпущены публично - это очень хорошая идея .

Гаурав
источник
7

(раскрытие: я автор)

После того, как вы все отсортировали, как описано выше, вы можете сделать следующий шаг и использовать Devel :: CoverX :: Covered, например,

  • Для исходного файла перечислите тестовые файлы, которые обеспечивают покрытие этого исходного файла. Это может быть сделано на уровне файла, подпрограммы и строки.
  • Для тестового файла перечислите исходные файлы и подпрограммы, охватываемые этим тестовым файлом.
  • Учитывая исходный файл, эффективно составляйте отчет о деталях покрытия для каждой строки или подпрограммы.

См. Синопсис для конкретных примеров командной строки.

В Devel :: PerlySense есть поддержка Emacs для отображения информации о покрытии в буфере исходного кода ( снимок экрана ) и для перехода к закрывающим тестовым файлам и обратно.

jplindstrom
источник