Есть ли способ издеваться над встроенной функцией require в Perl? - PullRequest
4 голосов
/ 09 сентября 2011

Я занимаюсь разработкой приложения, которое должно заменить существующий беспорядок кусочка спагетти-кода на кусочек. Для достижения этой цели у меня есть диспетчер, который запускает необходимые ресурсы HTTP, когда URI был сопоставлен, и иначе использует устаревший класс ресурсов HTTP.

Итак, этот унаследованный ресурс HTTP должен require файл точки входа старой системы, и я пытаюсь выяснить, как протестировать этот процесс. Теперь я вижу, как мне хотелось бы заменить исходную функцию require на подпрограмму mock и проверить, что она была вызвана с соответствующим именем файла.

Возможно ли это, а если нет, может, есть лучший способ сделать это?

Ответы [ 3 ]

13 голосов
/ 09 сентября 2011

Для переопределения require в одном пакете:

use subs 'require';  # imports `require` so it can be overridden

sub require {print "mock require: @_\n"}

Для переопределения require глобально:

BEGIN {
    *CORE::GLOBAL::require = sub {print "mock require: @_\n"}
}

И затем:

require xyz;           # mock require: xyz.pm

require Some::Module;  # mock require: Some/Module.pm
10 голосов
/ 10 сентября 2011

Лучший способ переопределить require в глобальном масштабе - установить хук в @INC. Эта малоизвестная функциональность описана в конце документации require .

Вот простой пример, который перехватывает любой запрос для модуля, имя которого начинается с HTTP:

BEGIN {
  unshift @INC, sub {
    my ($self, $file) = @_;
    return unless $file =~ /^HTTP/;
    print "Creating mock $file\n";
    my @code = "1"; # Fake module must return true
    return sub { $_ = shift @code; defined $_ };
  }
}

require HTTP::Foo;
use HTTPBar;

Обратите внимание, что это также высмеивает use, поскольку оно основано на require.

2 голосов
/ 10 сентября 2011

Что-то еще, что вам нужно знать. Это либо дополняет, либо заменяет необходимость переопределения require.

Знаете ли вы, что вы можете добавлять хуки в качестве ссылок на ваш путь @INC? Затем они будут применены глобально к операторам use и require.

Для цитирования perldoc требуется

Вы также можете вставить хуки в средство импорта, поместив код Perl непосредственно в массив @INC.

Существует три вида хуков: ссылки на подпрограммы, ссылки на массивы и благословенные объекты.

Ссылки на подпрограммы - самый простой случай. Когда система включения проходит через @INC и встречает подпрограмму, эта подпрограмма вызывается с двумя параметрами: первый ссылается на себя, а второй - на имя файла, который нужно включить (например, «Foo / Bar.pm») , Подпрограмма должна возвращать либо ничего, либо список из трех значений в следующем порядке:

1. Файловый дескриптор, из которого будет считан файл.

2. Ссылка на подпрограмму. Если нет файлового дескриптора (предыдущий элемент), то эта подпрограмма должна генерировать одну строку исходного кода за вызов, записывая строку в $ _ и возвращая 1, а затем, наконец, в конце файла, возвращая 0. Если есть файловый дескриптор, тогда подпрограмма будет вызываться, чтобы действовать как простой фильтр источника со строкой, читаемой в $ _. Снова верните 1 для каждой допустимой строки и 0 после того, как все строки были возвращены.

3. Дополнительное состояние для подпрограммы. Состояние передается как $ _ [1]. Ссылка на саму подпрограмму передается как $ _ [0]

Вот пример:

#!/usr/bin/perl

sub my_inc_hook {
    my ($sub_ref, $file) = @_;

    unless ($file =~ m{^HTTP/}) {
        warn "passing through: $file\n";
        return;
    }

    warn "grokking: $file\n";
    return (\*DATA);
}

BEGIN {
    unshift(@INC, \&my_inc_hook);
}

use strict;
require warnings;
require HTTP::Bazinga;

HTTP::Bazinga::it_works();

__DATA__
package HTTP::Bazinga;

sub it_works {warn "bazinga!\n"};

1;

Производит:

$ perl inc.pl
passing through: strict.pm
passing through: warnings.pm
grokking: HTTP/Bazinga.pm
bazinga!

Я считаю, что это работает для Perl 5.10.0 и выше.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...