package Test::MockFile::Light;
use strict;
use Carp;
# ---
my %file;
sub define_file_mocker {
my ($name, $content) = @_;
$content ||= '';
$file{$name} = $content;
}
sub undefine_file_mocker {
my ($name, $content) = @_;
delete $file{$name};
}
# ---
sub import {
my ($class, %arg) = @_;
my $package = caller;
_export_functions_to($package);
my $module = defined $arg{module} ? $arg{module} : $package;
_override_open_builtin_for($module);
}
# ---
sub _export_functions_to {
my ($package) = @_;
no strict 'refs';
*{"$package\::define_file_mocker"} = \&define_file_mocker;
*{"$package\::undefine_file_mocker"} = \&undefine_file_mocker;
}
# ---
sub _override_open_builtin_for {
my ($module) = @_;
my $mode_and_name_parser = qr/
^
\s*
(|<|>|>>|\+<|\+>|\+>>) # the mode
\s*
(\w.*?) # the file name
\s*
$
/x;
no strict 'refs';
*{"$module\::open"} = sub (\[*$]$;$) {
my ($fh, $mode, $name) = @_;
$name ||= '';
my $compound = "$mode $name";
if ($compound =~ $mode_and_name_parser) {
$mode = $1 || '<';
$name = $2;
}
else {
croak 'Unexpected open() parameters for file mocking';
}
if ($mode eq '<' && ! defined $file{$name}) {
$! = 2;
return 0;
}
return open $$fh, $mode, \$file{$name};
};
}
# ---
1;
####
package Foo::Bar;
sub process {
my ($class, $file_name) = @_;
my $sum = 0;
open my $fh, $file_name;
while (<$fh>) {
chomp;
$sum += $_;
}
close $fh;
return $sum;
}
1;
####
use Test::More tests => 3;
use Test::MockFile::Light module => 'Foo::Bar';
use_ok('Foo::Bar');
my $file_name = 'data.txt';
my $content = join("\n", 1..5);
define_file_mocker($file_name, $content);
can_ok(Foo::Bar, 'process');
is(Foo::Bar->process($file_name), 15, 'unexpected sum');