Suppose I have a class like this:
class MyClass {
method data-is-valid {
return self!get-data ~~ m{^From};
}
method !get-data {
return 'From Internet';
}
}
where !get-data
method gets some data from Internet.
Is it possible to mock that method so that it returns my own hardcoded data so I can test the module without connecting to the Internet?
Ideally, the solution should not modify the definition of the class in any way.
NOTE: A similar question exists regarding unittesting subroutines of modules.
Mocking is done when you invoke methods of a class that has external communication like database calls or rest calls. Through mocking you can explicitly define the return value of methods without actually executing the steps of the method.
Mocking means creating a fake version of an external or internal service that can stand in for the real one, helping your tests run more quickly and more reliably. When your implementation interacts with an object's properties, rather than its function or behavior, a mock can be used.
I would first refactor to pull the fetching logic out to a different object, and make MyClass
depend on it:
class Downloader {
method get-data {
return 'From Internet';
}
}
class MyClass {
has Downloader $.downloader .= new;
method data-is-valid {
return $!downloader.get-data ~~ m{^From};
}
}
This is an example of dependency inversion, which is a helpful technique for making code testable (and tends to make it easier to evolve in other ways too).
With this change, it is now possible to use the Test::Mock module to mock Downloader
:
use Test;
use Test::Mock;
subtest 'Is valid when contains From' => {
my $downloader = mocked Downloader, returning => {
get-data => 'From: blah'
};
my $test = MyClass.new(:$downloader);
ok $test.data-is-valid;
check-mock $downloader,
*.called('get-data', :1times);
}
subtest 'Is not valid when response does not contain From' => {
my $downloader = mocked Downloader, returning => {
get-data => 'To: blah'
};
my $test = MyClass.new(:$downloader);
nok $test.data-is-valid;
check-mock $downloader,
*.called('get-data', :1times);
}
You probably want to take a look at Test::Mock. From its SYNOPSIS:
use Test;
use Test::Mock;
plan 2;
class Foo {
method lol() { 'rofl' }
method wtf() { 'oh ffs' }
}
my $x = mocked(Foo);
$x.lol();
$x.lol();
check-mock($x,
*.called('lol', times => 2),
*.never-called('wtf'),
);
Hi @julio i suggest you take a look at the wrap function for routines, this should do what you need... https://docs.raku.org/language/functions#Routines ... this includes use soft; pragma to prevent inlining
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With