How to reuse mock objects in Perl unit tests? - perl

I have a bunch of unit tests for my module, and I find that I copy the same mock and setup code from one to the other. How can I DRY that up and reuse the mock code?
I've placed this simple mock object next to my tests:
package MockObject;
1;
If I just say use MockObject; in the test case, make test cannot find MockObject. Makes sense; after all, it's not installed system wide and it's not next to the module under test.
I can run my tests with prove -I lib -I t t/*.t but I'd like to keep make test, if only for the laziness of typing a few characters less.
Since the mock object isn't a full module and shouldn't be officially installed anyway, I cannot and don't want to set TEST_REQUIRES in Makefile.PL.
Adding test => { FILES => 't/*.t', INC => 't/' } to Makefile.PL didn't help.
How can I (simply) reuse Perl mock code with the MakeMaker generated Makefile?

Setup PERL5LIB or add use lib in your script. I found FindBin package useful for setting up the lib path for tests.
use FindBin qw($Bin);
use lib "$Bin/../lib";
Where:
$Bin - path to bin directory from where script was invoked

Related

'use lib' can't find Perl module

I'm on Windows 7, I downloaded a CPAN module called XML::XPath, and I want to use it in a script I wrote.
I cannot modify #INC on my machine, I cannot modify environmental variables on my machine, I cannot run make on my machine, and I cannot use a package manager on my machine.
So in order to use the module, I went to the CPAN website, downloaded the module's .tar.gz file, and unzipped it into a lib folder in my project. I did that because this guide suggested that I can use use lib in the context of my script to reference the downloaded module:
Adding a use lib statement to the script will add the directory to #INC for that specific script. Regardless who and in what environment runs it.
My script is called test.pl. I am trying to use the XML::XPath module to parse an XML file called test.xml. Here is an example of my directory structure:
C:/
→ sandbox/
test.pl
test.xml
→ lib/
→ XML-XPath-1.13/
XPath.pm
→ XPath/
XMLParser.pm
(etc.)
This is my test.pl script:
#!/usr/bin/perl
use strict;
use warnings;
use lib qw("C:/sandbox/lib/XML-XPath-1.13");
my $xml_file = "C:/sandbox/test.xml";
my $result;
if (-f $xml_file) {
$result = XML::XPath->new(filename => "$xml_file");
}
When I run this script with perl test.pl, the script fails with the following error:
Can't locate object method "new" via package "XML::XPath" (perhaps you forgot to load "XML::XPath"?) at test.pl line 10.
Can I resolve this error with use lib?
EDIT:
When I add use XML::XPath; to my test.pl script, as such:
#!/usr/bin/perl
use strict;
use warnings;
use lib qw("C:/sandbox/lib/XML-XPath-1.13");
I get an error like:
Can't locate XML/XPath.pm in #INC
But as stated above, I cannot physically modify #INC or recompile Perl on my machine.
This is not going to work. XML::XPath depends on the XML::Parser module which needs to be compiled. Since you do not have a complete toolchain with a C compiler available, there is no way you are getting this to run. You will have to search for a different way to handle your XML.
Just extracting a tarball only works when you have a pure-Perl module.
The easiest way to get a full Perl toolchain on Windows is to install Linux install the Strawberry Perl distribution. There's even a ZIP edition and a portable edition that can be installed on a thumb drive, no admin privileges required.
Your more immediate problem is that XML::XPath in version 1.13 seems to be a shoddy module with an unusual directory layout. The package you are trying to load is at XML-XPath-1.13/XPath.pm but should be at XML-XPath-1.13/lib/XML/XPath.pm. Why?
When you say use XML::XPath; this does two things. First, Perl searches for a file XML/XPath.pm in all #INC directories. Assuming you have something like
use FindBin;
use lib "$FindBin::Bin/lib/XML-XPath-1.13";
then perl would expect a file PROJECT/lib/XML-XPath-1.13/XML/XPath.pm which doesn't exist (where I'm using PROJECT as placeholder for the directory of your script).
Since the file PROJECT/lib/XML-XPath-1.13/XPath.pm does exist you could say use XPath;.
But then the second thing happens: The package is imported so that it can load subroutines or constants into your namespace. The use'd package name is now used as a class: XPath->import. However, the XPath.pm file does not contain the XPath package but the XML::XPath package, so there is no XPath class with an import method.
The import method call can be suppressed by providing an empty list in the use statement: use XPath ();.
Better than such hacks, you could use a more recent version of XML::XPath (version 1.13 is from 2003, but the newer version 1.42 is from 2007). Once you extract it you could use lib "$FindBin::Bin/lib/XML-XPath-1.42/lib" and then use XML::XPath;.
But this still won't work, because XML::XPath does use XML::Parser and you don't have that module installed. Now we are back to your main problem: XML::Parser needs to be compiled first because it contains a C extension, and also relies on an external library. Without a full toolchain you can't compile it.
First of all, you should properly install the module into lib rather than unzipping the distribution into lib.
Then, you'll need
use FindBin qw( $RealBin );
use lib "$RealBin/lib";
use XML::XPath qw( );

Perl Par::Packer Can't find module issue

I have a perl program that uses WWW::Mechanize::Firefox on windows 7 32bit with strawberry perl.
It works fine with the command C:\>perl testcase.pl. When I compile it with C:\>pp -o testcase.exe testcase.pl it compiles with no errors.
When I run the testcase.exe it gives me the error:
Failed to connect to , Can't locate object method "setup" via package "MozRepl::Client" at MozRepl.pm line 224
The code I am using for testcase.pl is:
#!perl
use MozRepl;
use WWW::Mechanize::Firefox;
use warnings;
system('start firefox');
sleep(5);
$mech = WWW::Mechanize::Firefox->new;
Also note that a program without WWW::Mechanize::Firefox and MozRepl does work fine.
The problem has obviously been narrowed down to PAR::Packer not liking MozRepl, any idea what it might be?
PAR::Packer sometimes has a hard time identifying which modules need to be included in a PAR package in order to fulfill all of the requirements of the program you are trying to package.
It copes OK if the dependancies are loaded via plain 'use', or 'require' statements where the module to be loaded is a literal string, but it won't have much chance if the module is being loaded dynamically with something like:
require $myModuleToLoad;
Browsing the source code of MozRepl and related modules shows that they make heavy use of plugins loaded dynamically. I suspect that some of these are not being packaged.
You can manually specify module(s) to be included in the PAR package by adding -M Module::Name to the pp command line for each of the modules to be added (replacing Module::Name with the actual module name of course).
The hard part might be identifying which modules to include. One way to do this is to temporarily add something like this to the end of your script:
END { print "$_ -> $INC{$_}\n" foreach sort keys %INC; }
then run your script normally, not through PAR. It should list all the modules that were loaded. You can compare that with the actual modules present in the PAR package and add the missing ones using the -M option to pp.
You can see the modules inside your PAR file by opening it with an unzipping tool, such as 7zip. Or in Linux:
unzip -l {parfile}

MakeMaker update its list of files at `make` time

I'm working on a Perl module called Mite. It is a "compiler", of sorts. You write Perl classes using a Moose-like declarative OO syntax. Rather than doing all the work to put the class together every time it's executed, Mite does that work at build time. It generates an extra file containing the Perl code for your accessors and inheritance and whatnot.
This extra file is put into lib with the rest of your code and released with your project. As a result, the installing user does not need to install Mite and the code loads faster.
During development, the mite compiler is run when make or Build is run. So things like make test and ./Build test just work. This is accomplished by using special shims for MakeMaker or Module::Build.
This works fine for Module::Build, but ExtUtils::MakeMaker does not see the mite files. MakeMaker hard codes a list of what is in lib when Makefile.PL is run. The pm_to_lib step then fails to copy the generated files into blib where make test will see them.
How can I best work around this problem? I wish the process to remain transparent to the developer (once they've loaded the appropriate shim), and require no special dependencies for the installing user.
UPDATE: Here is a clearer example. Let's say you have a project like this.
Makefile.PL
lib/
Foo.pm
Bar.pm
Foo/
Thing.pm
t/
foo.t
bar.t
You run perl Makefile.PL and then make. The make step has been modified to generate an extra .mite.pm file for each .pm file. After the make step, what I want is this.
Makefile.PL
Makefile
lib/
Foo.pm
Foo.pm.mite.pm
Bar.pm
Bar.pm.mite.pm
Foo/
Thing.pm
Thing.pm.mite.pm
blib/
lib/
Foo.pm
Foo.pm.mite.pm
Bar.pm
Bar.pm.mite.pm
Foo/
Thing.pm
Thing.pm.mite.pm
t/
foo.t
bar.t
All the new files introduced into lib have been copied into blib/lib where they can be seen as part of make test. What I get instead is this.
Makefile.PL
Makefile
lib/
Foo.pm
Foo.pm.mite.pm
Bar.pm
Bar.pm.mite.pm
Foo/
Thing.pm
Thing.pm.mite.pm
blib/
lib/
Foo.pm
Bar.pm
Foo/
Thing.pm
t/
foo.t
bar.t
That is because the Makefile is generated by Makefile.PL with a hard coded list of what is in lib.
(This is particularly silly, I maintained MakeMaker for 10 years and failed to fix this.)
I wound up adding a new target and having pm_to_blib depend on it. The new target just moves all .pm files from lib/ to blib/lib/. The redundancy shouldn't matter.
I'm not happy with this solution, but it appears to work.
https://github.com/evalEmpire/Mite/commit/feff24e4d68e062a06a721591ff0d785c5dad80b

Can I build Perl modules with ExtUtils::MakeMaker-based build system "out of tree"?

Instead of adding or modifying files in the directory where the sources of a Perl module are unpacked, I would like to build everything in a separate directory. Is this easily achievable with a fairly standard Makefile.PL that uses ExtUtils::MakeMaker? (By easy, I mean something like one or a few command line parameters.) If no, does any of the other build systems support this?
Update / Reason: The Perl module is a binding to a library whose build system is autoconf/automake/libtool-based. The Perl module is shipped together with this library and calling make in the top directory eventually also builds the Perl library. I am interested in building the entire project in a separate build tree. At the moment I do something similar to what runrig suggested, only using symlinks. (cp -sru $(srcdir)/. $(builddir)/.). This has worked so far, but if there is a more elegant solution, I'd like to read about it.
MakeMaker already copies the sources and builds them in a separate directory (that's what blib/ is). You can control the build location with the INST_* set of arguments to WriteMakefile(). This example changes the location from blib/ to foo/.
INST_ARCHLIB => "foo/arch",
INST_LIB => "foo/lib",
INST_BIN => "foo/bin",
INST_SCRIPT => "foo/script",
INST_MAN1DIR => 'foo/man1',
INST_MAN3DIR => 'foo/man3',
In addition you have to tell MakeMaker to cleanup the new build directory.
clean => {
FILES => 'foo'
},
See "Determination of Perl Library and Installation Locations" in the ExtUtils::MakeMaker docs for more info.
cp -R Module-Directory-0.01 Module-Directory-0.01.copy
cd Module-Directory-0.01.copy
perl Makefile.PL
make
make test
...etc.
I ended up using symlinks:
The library to which the Perl module provides bindings uses an
autoconf/automake/libtool-based build system. Makefile.PL is
generated from Makefile.PL.in by configure. Makefile.PL
generates Makefile-pl (Makefile has already been taken by
autoconf/automake).
This is the relevant part of Makefile.am:
all: Makefile-pl src_deps
$(MAKE) -f Makefile-pl
Makefile-pl: Makefile.PL
perl Makefile.PL INSTALLDIRS=$(INSTALLDIRS) PREFIX=$(prefix)
I changed the second target to:
Makefile-pl: Makefile.PL
-[ $(srcdir) != $(builddir) ] && cp -rsu $(abs_srcdir)/. $(builddir)/.
perl Makefile.PL INSTALLDIRS=$(INSTALLDIRS) PREFIX=$(prefix)
This should work as long as building or installing the Perl module
does not result in any files being modified in-place.

How can I use vendor-packaged modules from a Perl I compiled myself?

My OS-Distribution provides the rpm-package "perl-obexftp", which installs the Modul "OBEXFTP".
These are the files:
/usr/lib/perl5/vendor_perl/5.10.0/x86_64-linux-thread-multi/OBEXFTP.pm
/usr/lib/perl5/vendor_perl/5.10.0/x86_64-linux-thread-multi/auto/OBEXFTP
/usr/lib/perl5/vendor_perl/5.10.0/x86_64-linux-thread-multi/auto/OBEXFTP/.packlist
/usr/lib/perl5/vendor_perl/5.10.0/x86_64-linux-thread-multi/auto/OBEXFTP/OBEXFTP.bs
/usr/lib/perl5/vendor_perl/5.10.0/x86_64-linux-thread-multi/auto/OBEXFTP/OBEXFTP.so
/var/adm/perl-modules/obexftp
I am using now a Perl which I have build from the source.
Is there a simple way to make this OBEXFTP-module accesseble to my Perlinstallation?
Choose one of
Add the following pragma to your code:
use lib '/usr/lib/perl5/vendor_perl/5.10.0';
Add that path to the PERL5LIB environment variable
Invoke your code with perl -I/usr/lib/perl5/vendor_perl/5.10.0 program
Rebuild perl so that path is in its baked-in #INC
Build the module yourself using your custom-built perl
For details, see perlrun.