How can I prevent Perl from using the module for testing purposes?

I develop a set of Perl scripts and modules that are then deployed to different machines and systems around our company. Some objects depend on a specific module, which may or may not be installed on different machines. I used 'eval' to determine if this module is available.

I just had a malfunction report, which boiled down to the fact that the user was unable to install the module on his machine (but did not understand that he did not): but the error in my code was that in this case I didn’t accepted the error condition to the top level, so it was lost, and the script simply did not execute part of its function.

To examine it, I disabled a specific module on my machine and easily found and fixed the problem. But the only way I could turn it off without deleting it was to rename the file (which I had to do through sudo, of course).

Now I am running all my tests with this module unavailable, and it has launched several other places where I am not handling the situation correctly.

But now I want to write some tests for this condition: but how can I reasonably make this module temporarily unavailable as part of an automatic test. I really don't want my tests to use sudo to move modules (I can do other things on the machine at the same time).

Does anyone know a way that I can tell Perl "Don't find this module, wherever I try to" use "or" require "it, for testing purposes"?

I am running Perl 5.10.0 (on Fedora 12) and using Test :: More and TAP :: Harness. Perl 5.8 works in some of our installations, so I am ready to use the 5.10 functions when testing, but not in the code itself.

+4
source share
2 answers

There a couple of CPAN modules does just that. I often use Test::Without::Module . The other is Devel::Hide . These two and several others, whose names I can’t remember right now, all work almost the same way, connecting to loading the perl module via @INC or CORE::GLOBAL::require . Details of this document are described in perldoc -f require .

+10
source

As rafl said, there are modules for this.

If you are interested in its mechanics (in addition to achieving a result), there are two ways to do this:

  • Remove the module from the namespace after loading it. Test :: Without :: Module does this - take a look at the source code for details.

  • Prevent module loading in the first place. The simplest approach is to take advantage of Perl's ability to have routines as part of the @INC array, which is used when loading modules through use / require . The theory underlying this can be found in requiring perldoc - to search for text for the word “hooks”.

Subroutines are the simplest thing to do. When the inclusion system goes through @INC and a subroutine is found, this subroutine gets called two parameters, the first link to itself, and the second name of the file to be included (for example, "Foo / Bar.pm"). A routine should not return a list of three values ​​...

... 2. Link to the subroutine. If there is no file descriptor (previous element), then this routine is expected to generate one line of source code for each call, writing a line to $ _ and returning 1, and then returning 0 to the end of the file.

So what you do is write sub, which (only for specified packages) returns an empty code.

 # The following code was not tested - for illustrative purposes only # MUST be done in the BEGIN block at the very beginning of the test # BEFORE any "use Module"; lines push @INC, \&my_sub; my %prohibited_module_files = map { $_=> 1} ("Foo/Bar.pm", "x.pm"); # Ideally, translate module names into file names sub empty_module_sub { $_ = "1;\n"; # Empty module return 0; # End of file } sub my_sub { my ($coderef, $filename) = @_; # $coderef is \&my_sub if ($prohibited_modules{$filename}) { print STDERR "NOT loading module $filename - prohibited!\n"; # Optionally, die here to simulate not finding the module!!! # Otherwise, load empty package return (undef, \&empty_module_sub); } return undef; # Continue searching @INC for good modules. } 

A somewhat simpler (but not so interesting or flexible) approach will be based on the fact that the semantics of “requires” first checks $INC{$filename} and, if this key exists in the% INC hash, it considers that the module is loading; if the key is matched with the true value, it considers that the module has already been loaded correctly, and false, it dies with an error like “compilation”. Thus, you can achieve a result similar to the custom sub, above bt, by inserting undef or 1 under the corresponding key (the file name corresponding to the module name) in %INC in the BEGIN block at the beginning of your code.

+4
source

All Articles