Perl: mocking -d -f и друзья. Как поместить их в CORE:: GLOBAL
Документация CORE показала мне, как весело издеваться над различными встроенными функциями Perl. Тем не менее, я не совсем уверен, как заменить "-d" и c. с моими методами. Так что это действительно вопрос о том, как заменить функцию чертой в CORE:: GLOBAL.
Было бы удобно использовать справочную ссылку.
package Testing::MockDir;
use strict;
use warnings;
use Exporter();
use Symbol 'qualify_to_ref';
*import = \&Exporter::import;
our @EXPORT_OK = qw(SetMockDir UnsetMockDir);
our %EXPORT_TAGS = (
'all' => \@EXPORT_OK,
);
my %path2List = ();
my %handle2List = ();
BEGIN {
*CORE::GLOBAL::opendir = \&Testing::MockDir::opendir;
*CORE::GLOBAL::readdir = \&Testing::MockDir::readdir;
*CORE::GLOBAL::closedir = \&Testing::MockDir::closedir;
######################### the "-" is really the problem here
*CORE::GLOBAL::-d = \&Testing::MockDir::mock_d; # This does not work <<<<<
}
sub mock_d ($) {
die 'It worked';
}
sub SetMockDir {
my ($path, @files) = @_;
$path2List{$path} = [@files];
}
sub UnsetMockDir {
my ($path) = @_;
delete $path2List{$path};
}
sub opendir (*$) {
my $handle = qualify_to_ref(shift, caller);
my ($path) = @_;
return CORE::opendir($handle, $path) unless defined $path2List{$path};
$handle2List{$handle} = $path2List{$path};
return 1;
}
sub readdir (*) {
my $handle = qualify_to_ref(shift, caller);
return CORE::readdir($handle) unless defined $handle2List{$handle};
return shift @{$handle2List{$handle}} unless wantarray;
my @files = @{$handle2List{$handle}};
$handle2List{$handle} = [];
return @files;
}
sub closedir (*) {
my $handle = qualify_to_ref(shift, caller);
return CORE::closedir($handle) unless defined $handle2List{$handle};
delete $handle2List{$handle};
return 1;
}
1;
Ответы
Ответ 1
Это может быть невозможно. perlsub раздел Переопределение встроенных функций неопределенно, какие функции можно переопределить. "Многие" могут "некоторые" не могут, но, помимо нескольких примеров, нет окончательного списка.
Обычно я бы попробовал это:
{
no strict 'refs';
*{'CORE::GLOBAL::-d'} = \&Testing::MockDir::mock_d;
}
который не является синтаксической ошибкой, но не имеет эффекта переопределения -d
.
Ответ 2
CORE:: GLOBAL не работает над вещами без прототипов. Единственный способ, которым я могу это сделать, это переписать дерево кода операций... которое не для слабонервных. Вы можете сделать это с помощью комбинации B::Utils и B::Generate и много экспериментов.
Проще всего было бы использовать File::Temp, чтобы создать временную структуру каталогов по вашему вкусу.
Ответ 3
Спасибо всем за ваши ответы.
То, что я запустил, - на основе каждого модуля/цели тестирования, я использовал код с "-d" в него в свою собственную функцию. Так вот...
# Because I cannot mock -d directly
sub dirExists {
return -d shift;
}
Затем я могу заменить эту функцию в тестовом модуле с помощью
my $doesDirExist = 1;
*MyModule::dirExists = \&main::mock_dirExists;
sub mock_dirExists {
return $doesDirExist;
}
Это довольно уродливо, но я не хотел зависеть от этого слишком долго, и он хорошо работает enuf для моих целей
Ответ 4
Проблема в том, что ваше приложение зависит от жестко заданных спецификаций файлов. Вы должны параметризовать спецификации файла; то вам больше не нужно издеваться над собой, вы можете просто использовать Directory:: Scratch или что-то в этом роде.
Ответ 5
Вы можете пойти по маршруту фильтра источника:
package Testing::MockDir;
use Filter::Simple;
FILTER { s/\s+\-d (\S+)/ Testing::MockDir::filetest 'd',$1/g };
sub filetest {
my ($test, $file) = @_;
print "Mocking -$test $file\n";
return 1;
}
(Этот пример кода не очень надежный. Например, он не будет транслировать -d$dir
или -d "dirname with spaces"
, но вы можете усилить его до тех пор, пока он не удовлетворит потребности вашего целевого кода).