1

I have this function that I want to test:

use constant NEXT => 'next';
use constant BACK => 'back';

sub getStringIDs {
    return [
        NEXT,
        BACK
    ];
}

I've tried to write the following test, but it fails:

subtest 'check if it contains BACK' => sub {
    use constant BACK => 'back';
    my $strings = $magicObject->getStringIDs();
    ok($strings =~ /BACK/);
}

What am I doing wrong?

justHelloWorld
  • 6,478
  • 8
  • 58
  • 138

3 Answers3

1

The return value of $magicObject->getStringIDs is an array reference, not a string. It looks like the spirit of your test is that you want to check if at least one element in the array pattern matches BACK. The way to do this is to grep through the dereferenced array and check if there are a non-zero number of matches.

ok( grep(/BACK/,@$strings) != 0, 'contains BACK' );

At one time, the smartmatch operator promised to be a solution to this problem ...

ok( $strings ~~ /BACK/ )

but it has fallen into disrepute and should be used with caution (and the no warnings 'experimental::smartmatch' pragma).

mob
  • 117,087
  • 18
  • 149
  • 283
1

Your getStringIDs() method returns an array reference.

The regex binding operator (=~) expects a string on its left-hand side. So it converts your array reference to a string. And a stringified array reference will look something like ARRAY(0x1ff4a68). It doesn't give you any of the contents of the array.

You can get from your array reference ($strings) to an array by dereferencing it (@$strings). And you can stringify an array by putting it in double quotes ("@$strings").

So you could do something like this:

ok("@$strings" =~ /BACK/);

But I suspect, you want word boundary markers in there:

ok("@$strings" =~ /\bBACK\b/);

And you might also prefer the like() testing function.

like("@$strings", qr[\bBACK\b], 'Strings array contains BACK');

Update: Another alternative is to use grep to check that one of your array elements is the string "BACK".

# Note: grep in scalar context returns the number of elements
# for which the block evaluated as 'true'. If we don't care how
# many elements are "BACK", we can just check that return value
# for truth with ok(). If we care that it's exactly 1, we should
# use is(..., 1) instead.

ok(grep { $_ eq 'BACK' } @$strings, 'Strings array contains BACK');

Update 2: Hmm... the fact that you're using constants here complicates this. Constants are subroutines and regexes are strings and subroutines aren't interpolated in strings.

Dave Cross
  • 68,119
  • 3
  • 51
  • 97
  • May I ask you why you use `"@$strings"` and not simply `@$strings` (without quotes)? – justHelloWorld Jun 21 '18 at 15:25
  • And also, why do you use quotes in `$_ eq 'BACK'` – justHelloWorld Jun 21 '18 at 15:27
  • 1
    Using `$strings` runs all the elements together (`NEXTBACK`) whereas `"@strings"` puts spaces between the elements (`NEXT BACK`). That allows us to use `\b` to look for individual words. Otherwise, we could be in trouble if our array contained "FEB" and "ACK". – Dave Cross Jun 21 '18 at 15:28
  • @just the quotes here in Dave's code relate to my comment on your question. When you use `m/BACK/` that's the string _BACK_, not the function call (constants are nothing more than functions in Perl). Perl can't interpolate function calls in strings. – simbabque Jun 21 '18 at 15:28
1

The in operator is your friend.

use Test::More;
use syntax 'in';

use constant NEXT => 'next';
use constant BACK => 'back';

ok BACK |in| [NEXT, BACK], 'BACK is in the arrayref';
done_testing;
daxim
  • 39,270
  • 4
  • 65
  • 132
  • You should probably mention that they'll need to install the [syntax](https://metacpan.org/pod/syntax) and [Syntax::Feature::In](https://metacpan.org/pod/Syntax::Feature::In) modules. – Dave Cross Jun 25 '18 at 07:49