Perl - статьи

       

Вначале пишите тесты, затем код


Пожалуй, наиболее универсальный принцип программирования — вначале писать комплект тестов.

Комплект тестов — это исполняемая спецификация какого-либо компонента программного обеспечения, автоматически контролирующая его поведение. Если у Вас есть комплект тестов, Вы можете — в любой точке процесса разработки — проверить, что код работает так, как вы того ожидаете. Если у Вас есть комплект тестов, Вы можете — после любых изменений во время цикла поддержки — удостовериться, что код по прежнему работает как полагается.

Вначале пишите тесты. Напишите их как только Вы определитесь с программным интерфейсом (см. совет #1). Пишите их до того, как начали реализацию Вашего приложения или модуля. До тех пор, пока у Вас нет тестов, у Вас нет чёткой спецификации функционала Вашего ПО, и тем более нет возможности узнать соответствует ли поведение ПО этой спецификации.

Написание тестов всегда кажется рутиной, причём рутиной в данном случае бессмысленной: у Вас ещё нет ничего, что можно тестировать - зачем писать эти тесты? Однако большая часть разработчиков будет -- почти "на автомате" -- писать вспомогательное ПО для тестирования их новых модулей для конкретных случаев (ad hoc):

> cat try_inflections.pl

# Тест для моего нового супер-модуля Английской морфологии...

use Lingua::EN::Inflect qw( inflect );

# Формы множественного числа (стандартные и не очень)...

my %plural_of = ( 'house' => 'houses', 'mouse' => 'mice', 'box' => 'boxes', 'ox' => 'oxen', 'goose' => 'geese', 'mongoose' => 'mongooses', 'law' => 'laws', 'mother-in-law' => 'mothers-in-law', );

# Для каждого из слов, вывести вычисленный и ожидаемый результаты...

for my $word ( keys %plural_of ) { my $expected = $plural_of{$word}; my $computed = inflect( "PL_N($word)" );

print "Для $word:\n", "\tОжидается: $expected\n", "\tВычислено: $computed\n"; }

Специализированное вспомогательное ПО на самом деле написать сложнее чем набор тестов, поскольку Вы должны задумываться форматировании вывода, о представлении результатов в виде, удобном для анализа. Вспомогательное ПО также сложнее использовать чем набор тестов, поскольку каждый раз Вам необходимо анализировать выводимый результат "на глаз". Также такой способ контроля подвержен ошибкам. Наше зрение не оптимизировано для выявления небольших отличий в больших объёмах практически идентичного текста.




Вместо написания " на коленке" вспомогательной программы для тестирования, проще написать простые тесты используя стандартный модуль Test::Simple. Вместо операторов print для распечатки результатов, Вы просто вызываете функцию ok(), передавая ей в качестве первого аргумента булево значение или логическое выражение, проверяющее правильность вычислений, а в качестве второго аргумента описание того, что Вы тестируете:

> cat inflections.t

use Lingua::EN::Inflect qw( inflect);

use Test::Simple qw( no_plan);

my %plural_of = ( 'mouse' => 'mice', 'house' => 'houses', 'ox' => 'oxen', 'box' => 'boxes', 'goose' => 'geese', 'mongoose' => 'mongooses', 'law' => 'laws', 'mother-in-law' => 'mothers-in-law', );

for my $word ( keys %plural_of ) { my $expected = $plural_of{$word}; my $computed = inflect( "PL_N($word)" );

ok( $computed eq $expected, "$word -> $expected" ); }

Имейте в виду, что этот код загружает Test::Simple с аргументом qw( no_plan ). Обычно используется аргумент tests => count, обозначающий как много тестов ожидается. Но нашем случае тесты генерируются во время выполнения на основании данных таблицы %plural_of, так что окончательное число тестов будет зависеть от количества записей в таблице. Указание фиксированного числа тестов полезно в том случае, если Вы точно знаете число выполняемых тестов в момент компиляции, поскольку модуль может быть подвергнут "мета-тестированию": проверке того, что успешно выполнены все тесты.

Тестовая программа, использующая Test::Simple, более лаконична и читабельна, чем наша предыдущая вспомогательная программа, а вывод гораздо более компактный и информативный:

> perl inflections.t

ok 1 - house -> houses ok 2 - law -> laws not ok 3 - mongoose -> mongooses # Failed test (inflections.t at line 21) ok 4 - goose -> geese ok 5 - ox -> oxen not ok 6 - mother-in-law -> mothers-in-law # Failed test (inflections.t at line 21) ok 7 - mouse -> mice ok 8 - box -> boxes 1..8 # Похоже, 2 теста из 8-ми провалились.



Что более важно, эта версия программы требует гораздо меньше усилий для проверки результатов тестов. Вам нужно просто просканировать взглядом левый край текстового вывода программы на предмет наличия слова not.

Возможно, Вы предпочтёте использовать модуль Test::More вместо Test::Simple. Вы этом случае Вы сможете указывать отдельно полученные и ожидаемые значения, используя функцию is() вместо ok():

use Lingua::EN::Inflect qw( inflect ); use Test::More qw( no_plan ); # Теперь используем более продвинутый инструмент тестирования

my %plural_of = ( 'mouse' => 'mice', 'house' => 'houses', 'ox' => 'oxen', 'box' => 'boxes', 'goose' => 'geese', 'mongoose' => 'mongooses', 'law' => 'laws', 'mother-in-law' => 'mothers-in-law', );

for my $word ( keys %plural_of ) { my $expected = $plural_of{$word}; my $computed = inflect( "PL_N($word)" );

# Проверить вычисленные и ожидаемые словоформы на равенство... is( $computed, $expected, "$word -> $expected" ); }

Кроме того, что Вам теперь не нужно самому сравнивать значения с помощью eq, этот способ также позволяет получить более детальные сообщения об ошибках:

> perl inflections.t

ok 1 - house -> houses ok 2 - law -> laws not ok 3 - mongoose -> mongooses # Failed test (inflections.t at line 20) # got: 'mongeese' # expected: 'mongooses' ok 4 - goose -> geese ok 5 - ox -> oxen not ok 6 - mother-in-law -> mothers-in-law # Failed test (inflections.t at line 20) # got: 'mothers-in-laws' # expected: 'mothers-in-law' ok 7 - mouse -> mice ok 8 - box -> boxes 1..8 # Похоже, 2 теста из 8-ми провалились.

С Perl 5.8 поставляется документация Test::Tutorial -- введение в Test::Simple и Test::More.


Содержание раздела