tags:

views:

84

answers:

3

[start EDIT]

Yes, you can. See Michael Carman answer.

The initial question title "Is it possible to use a test like Test::More::cmp_ok but that accepts '~~' (smart match operator) with not a scalar at the 'expected' argument?" was idiotic and with the help of Brian d Foy it has become the current one. You can skip the rest of the question and go for the answers that are also about comparing deep structures.

The origin of this question was my belief that ~~ does not work with array or hashes refs (like keys for hashes) as I was so get use to see ~~ never used with references and the docs always mention @ and %. I got so obfuscated with that, that I made the newbie error of passing an array to a function intending to be a single argument :-( cmp_ok($known_valu, '~~', @returned, 'testing method abc'). My problem here was not cmp_ok but a better understanding of the smart match operator. The question is rubbish but you can learn good things from the answers.

[end EDIT]

I know that probably this is a 'XY question', so helping me to put the right question (or need) and the hint for the solution would also be appreciated.

The case:

I am testing the return of a function that produces an array. The return could be different depending of the environment but always it would have at least one constant value (the one that I want to test). I want to use a test like is not the ok in order to have better test info. As I am using Perl 5.12 and the smart match operator to find if the element is in the array, I can use:

ok($known_value ~~ @returned, 'testing method abc')

But I like the enhanced output of is or like with the found and expected part. So I tried the code that seemed more natural to me

cmp_ok($known_valu, '~~', @returned, 'testing method abc')

But it does not work because, seems that cmp_ok expects a scalar in both parts of the comparisons.

not ok 1 - testing method abc
#   Failed test 'testing method abc'
#   at abc.t line 53.
#     'stable_value'
#         ~~
#     '2'

The array in the expected slot is evaluated in scalar context and converted to '2'.

This makes sense because if it fails the 'expected' part can not be printed but should be print Dumper and probably this is not in the logic of this method.

I can solve it with a hack using like and stringifying the array, but having a test where you can use the smart match operator as a comparison method (like when) would be nice. Does someone know if Test::More can do it? or any other Test package?

At the moment I am using:

ok($known_value ~~ @returned, 'testing method abc')
  or diag (
      "ERROR:\n".
      "Found: ". Dumper @returned."\n".
      "Expected at least one element equal to '$known_value'"
  )

Is this the best that I can do?

+7  A: 

You can't use @returned because of how Perl passes arguments to subroutines. (Arrays are flattened into the argument list and lose their identity.) Pass an array reference instead:

cmp_ok($known_value, '~~', \@returned, 'testing method abc')

The smart match operator is smart enough to do the right thing. From perlsyn:

Note that the smart match implicitly dereferences any non-blessed hash or array ref, so the "Hash" and "Array" entries apply in those cases.

Michael Carman
Silly me! Before your editing I look again at the perlsync and I read `Note that the smart match implicitly dereferences any non-blessed hash or array ref, so the "Hash" and "Array" entries apply in those cases`. I have missed that note for all the times that I was looking there: After the first time I read it I always look at the (table)[http://perldoc.perl.org/perlsyn.html#Smart-matching-in-detail] where there is no example with references. Thanks a lot for fixing my overlooking.
Pablo Marin-Garcia
+2  A: 

Automatic dumping with Test::More, Test::Most:

use Test::More 0.82; diag explain \@returned;

use Test::Most 0.21; show \@returned;
daxim
Thanks daxim, I have seen that explain() has been in Test::More since 2008!! Probably I should reread the modules documentation more often to avoid missing nice new features ;-)
Pablo Marin-Garcia
+3  A: 

Test::Deep provides many utilities for testing parts of (possibly very deeply nested) structures, and also produces quite useful diagnostics in case of failures. I believe one of its bag functions could do the job for you.

use Test::Deep;

my @foo = ('bar', 'baz', 'moo');

cmp_deeply(
    \@foo,
    superbagof('baz'),
    '@foo contains at least one "baz"',
);

This way you can, if it ever turns out to be necessary, do much more complicated assertions than what smartmatching would allow without having to break things down into much smaller chunks, and will also continue to function on old perls.

rafl
Thanks, Rafl, nice to know this methods. I tried sometime ago Test::Deep->cmp_deeply for comparing two arrays, it was good but Test::More->is_deeply was better for me because I wanted to know where structures start to differ instead telling me they have different number of elements. I will give a more deeper read to documentation to see what I can add to my tool set from this module.
Pablo Marin-Garcia
@Pablo: `cmp_deeply` should tell you where the differences are. Also, `eq_or_diff` from Test::Differences is similar.
Ether
@Ether, yes and not, I like cmp_deeply and I started to use it, but then I realize that that seems that it is more interested in testing the whole equality than comparing, eg. in the case of two simple arrrays, if they have the same number of elements both `cmp_deeply` and `is_deeply` behaves the same, but if you have lets say `$a=[1,2]` and `$b=[1,2,3]` `cmp_deeply` tells you that they have different number of elements and stops, on the other hand `is_deeply` tell you always in which position the differences start . Do you know if this short-cut behaviour can be changed in `cmp_deeply`?
Pablo Marin-Garcia
@Pablo: without looking at the source or documentation for `cmp_deeply`, no I don't know.
Ether