[Moscow.pm] Test::Deep: проверка необязательных ключей хеша

Матюхин Вячеслав me на berekuk.ru
Чт Июл 19 06:26:12 PDT 2012


19.07.2012, 14:43, "Maxim Vuets" <maxim.vuets на gmail.com>:
> Привет!
>
> Test::Deep строг в проверке хешей. Во-первых, все заявленные ключи должны
> существовать, а их значения проходить проверку. Во-вторых, не должно быть
> лишних ключей. Мне нужен компромисс: проверять значения ключей, но только если
> они существуют (либо если их значения defined).
>
> Есть ли уже готовый код для этого?
>
> Я не нашёл, потому вижу следующее решение.
>
> Написать свой SC (special comparison) (например,
> Test::Deep::RequiredOptional), и использовать его для явного описания
> обязательных и опциональных ключей примерно так:
>
>     use Test::Deep::NoTest ':all';
>     my ($ok, $stack) = cmp_details(
>         {
>             login       => 'jsmith',
>             #password    => 'Hello1',
>             age         => 24,
>             #some       => 'rubbish',
>         },
>         req_opt(
>             # Required keys
>             {
>                 login       => re(qr/^[a-z0-9]+$/i),
>                 #password    => re(qr/^[\x21-\x7e]+$/),
>             },
>             # Optional keys
>             {
>                 age         => code(sub {(shift || 0) >= 18}),
>                 color       => any(qw/red green blue/),
>             }
>         )
>     );
>     say $ok ? 'ok' : ('fail: ', deep_diag($stack));
>
> Самая простая реализация такого SC будет транслировать $expected (просто
> используя уже существующие тесты) в следующий код:
>
>     all(
>         superhashof({
>             login       => re(qr/^[a-z0-9]+$/i),
>             #password    => re(qr/^[\x21-\x7e]+$/),
>         }),
>         subhashof({
>             login       => ignore(),
>             #password    => ignore(),
>             age         => code(sub {(shift || 0) >= 18}),
>             color       => any(qw/red green blue/),
>         }),
>     )
>
> Немного контекста. Я решаю проблему проверки сложной структуры, а не
> написание тестов. Потому готов рассмотреть альтернативные модули. Пока
> наиболее близкая альтернатива --- Params::Validate. Минус: делает только
> одноуровневою проверку (можно проверить, что значение ключа HASHREF, но
> не его содержимое). Контрплюс Test::Deep-а --- богатый декларативный
> синтаксис.
>
> Мысли, идеи, соображения?

Привет.
Можно использовать 'undef | ...':

$ perl -le 'use Test::Deep::NoTest; print eq_deeply({a => 1}, subhashof({ a => undef | re(qr/\d/), b => undef | re(qr/^[XYZ]$/) }))'
1
$ perl -le 'use Test::Deep::NoTest; print eq_deeply({b => "X"}, subhashof({ a => undef | re(qr/\d/), b => undef | re(qr/^[XYZ]$/) }))'
1
$ perl -le 'use Test::Deep::NoTest; print eq_deeply({ a => undef, b => "X"}, subhashof({ a => undef | re(qr/\d/), b => undef | re(qr/^[XYZ]$/) }))'
Use of uninitialized value in pattern match (m//) at /usr/share/perl5/Test/Deep/Regexp.pm line 57.
1
$ perl -le 'use Test::Deep::NoTest; print eq_deeply({a => "blah", b => "X"}, subhashof({ a => undef | re(qr/\d/), b => undef | re(qr/^[XYZ]$/) }))'
0

(Варнинг в третьем примере похож на баг, который можно и в Test::Deep пропатчить.)


Подробная информация о списке рассылки Moscow-pm