Search code examples
perltestingsmartmatch

Can I use smart matching, ~~, in Test::More's cmp_ok?


I am testing a function that returns an array. The array could be different depending on the environment but it will always have at least one constant value (the one that I want to test).

As I am using Perl 5.12, I can use the smartmatch operator to find if the element is in the array:

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

But I like the enhanced output of is and like with the "found" and "expected" parts. So I tried this:

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

This does not work because it seems that cmp_ok expects a scalar in both parts of the comparison:

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.

I can work around this with a hack using like and stringifying the array, but having a test where you can use the smartmatch operator as a comparison method (like when) would be nice. Is there a way to do this with Test::More or some other module?

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?


Solution

  • 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.