Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I test if a method is defined in a Perl 6 class?

Tags:

testing

raku

I often want to test that I've defined a method in a particular class. This has caught many problems where I've renamed a method or otherwise rearranged things in the architecture.

I know I can use .^lookup but that still feels weird to me like I'm eventually going to run into a case where it returns things in a different order than I expect (ignore signatures for now). This is what I came up with:

use Test;

class Foo is Str {}
class Bar is Str { method Str { 'Hello' } }

can-ok Str, 'Str';
can-ok Foo, 'Str';
can-ok Bar, 'Str';

is Foo.^lookup( 'Str' ).package.^name, 'Foo', 'Foo defines Str';
is Bar.^lookup( 'Str' ).package.^name, 'Bar', 'Bar defines Str';

done-testing;

It does what I want in this simple case and I haven't made it fail so far:

ok 1 - The type 'Str' can do the method 'Str'
ok 2 - The type 'Foo' can do the method 'Str'
ok 3 - The type 'Bar' can do the method 'Str'
not ok 4 -
ok 5 -
1..5
# Failed test at /Users/brian/Desktop/hello.p6 line 12
# expected: 'Foo'
#      got: 'Mu'
# Looks like you failed 1 test of 5
like image 359
brian d foy Avatar asked Mar 12 '18 22:03

brian d foy


1 Answers

You should not be comparing types by name.

my \Foo = anon class Foo {}
my \Bar = anon class Foo {}

say Foo.^name eq  Bar.^name; # True
say Foo       eqv Bar;       # False

In fact is checks for object identity if you give it a type object as the second argument.

is Bar.^lookup( 'Str' ).package, Bar, 'Bar defines Str'

You could always add a subroutine to add clarity.

sub defines-method (
  Mu:U $class,
  Str:D $method,
  Str:D $desc = "$class.^name() defines $method"
) {
  is $class.^lookup( $method ).?package, $class, $desc
}

defines-method Foo, 'Str';

You could alias it to an operator

sub &infix:<defines-method> = &defines-method;

Bar defines-method 'Str';

(Note that I used .?package in case .^lookup doesn't return anything.)


.^lookup gives you the Method object that will be called; so I don't know why you are talking about it giving you them in a different order when there is only one value returned. If there are multi methods it returns the proto method (possibly implicitly created).
If you want the individual multi methods you would call .candidates on it.
(There is also .^find_method, and off the top of my head I don't remember the difference)

I believe you are thinking of .can which gives you the Method objects in the order they would be called if you used .*Str or .+Str, which is the same as the method resolution order. Which means it would only change if you change the inheritance tree.

> class Bar is Str { method Str { 'Hello' } }

> quietly .perl.say for Bar.+Str;
"Hello"
""
""

> .perl.say for Bar.new.+Str
"Hello"
""
"Bar<80122504>"

> quietly .(Bar).perl.say for Bar.can('Str')
"Hello"
""
""

> .(Bar.new).perl.say for Bar.can('Str')
"Hello"
""
"Bar<86744200>"
like image 174
Brad Gilbert Avatar answered Nov 25 '22 18:11

Brad Gilbert