Perl tests - common parent for tests - perl

I have a set of tests, always named Module.t, each one starts like this:
use 5.026;
use strict;
use warnings;
use Test::Perl::Critic (-severity => 3);
use Module::Path 'module_path';
use Test::More tests => 8;
use Test::Log4perl;
Test::Log4perl->suppress_logging;
BEGIN { use_ok("My::Module") }
critic_ok(module_path("My::Module"));
... actual tests for this module ...
It's done this way because a bunch of modules are not coded very nicely and in effort to refactor stuff as we go, I'm trying to write tests for individual modules over time. Eg. I can't just enable Perl::Critic for all sources cause it will blow up in my face.
I would like to ideally make a "parent" test for all of these so that when me or a different developer wants to write a new test they will always have all the required stuff. Something like:
use 5.026;
use strict;
use warnings;
# 6 tests because 2 (use_ok and critic_ok) are already in the parent
use parent ParentTest("My::Module", tests => 6);
... actual tests for this module ...
Does perl have a way of doing that?
Disclaimer: I'm a perl noob, so maybe this has a better solution :-)

Sounds like you just want a helper module that loads some other modules and runs some initial tests for you.
Something like:
# ParentTest.pm
package ParentTest;
use strict;
use warnings;
use Test::Perl::Critic (-severity => 3);
use Module::Path 'module_path';
use Test::More;
use Test::Log4perl;
sub import {
my (undef, $module, %args) = #_;
$args{tests} += 2;
plan %args;
Test::Log4perl->suppress_logging;
use_ok $module;
critic_ok module_path $module;
#_ = 'Test::More';
goto +Test::More->can('import');
}
1
Usage would be:
use ParentTest "My::Module", tests => 6;
This is all untested, but the idea is:
We want to run some code to set up the initial test plan and run some tests.
We also want to export everything that Test::More exports, so our caller doesn't have to use Test::More themselves.
use Some::Module #args is equivalent to BEGIN { require "Some/Module.pm"; Some::Module->import(#args); }, so we can just put our custom logic in the import method.
We start by ignoring the first argument (which is a class name because import is called as a class method) and assigning the remaining arguments to $module and %args.
We increment $args{tests} by 2 to account for the two extra tests we perform automatically (if tests wasn't passed in, it is implicitly created here).
We pass %args to plan from Test::More, which is nice for setting up a test plan outside of the initial use line.
We perform the initial tests.
We tail call Test::More::import, erasing our own stack frame. This makes it look like our caller did Test::More->import(), which exports all the Test::More utility functions to them.
The unary + in goto +Test::More->... has no real effect, but it helps distinguish between the goto LABEL and goto EXPRESSION syntactic forms. We want the latter interpretation.

Related

Perl: Inheritance in modules - import and interfacing

I am trying to create my own module in perl that provides functions for data analysis out of a database.
I have several functions in EDL::Functions, eg. EDL::Functions::Average.
package EDL::Functions;
use warnings;
use strict;
package EDL::Functions::Average;
use parent "EDL::Functions";
sub new{...}
sub execute {...}
1) What do i have to add so use EDL::Functions; automatically imports all modules in EDL::Functions? Currently i have to import all submodules (in EDL::Functions)in order to make it work:
BEGIN {
our $VERSION = 5.20;
use EDL::Functions::Average;
use EDL::Functions::GetAllValues;
use EDL::Functions::GetValueStart;
use EDL::Functions::GetValueEnd;
use EDL::Functions::Min;
use EDL::Functions::Max;
use EDL::Functions::Median;
}
2) I want to make sure that if someone else builds his own function module the compilation will fail if it doesn't have the functions new and execute. How can i achieve that?
Thanks for your help!
It should work
perl -I ./ -MEDL::Functions -e 'EDL::Functions::Average->new();'
returns "I'm new".
cat EDL/Functions.pm
package EDL::Functions;
use warnings;
use strict;
package EDL::Functions::Average;
use parent "EDL::Functions";
sub new{ print "I'm new"; }
sub execute { print "Executing something";}
1;
But I prefere different structure, not declare several packages in one pm file. It'll save time for other devs and follows common practice, so EDL::Functions::Average should be at EDL/Functions/Average.pm

Can I include an argument within a code reference when calling Test::Output::stdout_like()

I'm writing unit tests using Test::More and Test::Output. I use Test::More to validate the return values and I plan to use Test::Output to validate the stdout produced by my subroutines.
I am attempting to write test cases for a subroutine whose stdout is dependent on the arguments sent. Test::Output::stdout_like(code reference, regexp, test description) looks to have the functionality I want, however I am struggling to construct a code reference which contains an argument.
I presume this is a common practice within Perl unit testing scripts. Can anyone offer an example?
Side note, thanks to Kurt W. Leucht for his Perl unit testing introduction: Perl build, unit testing, code coverage: A complete working example
No you can't directly include an arg within a coderef.
To pass an arg to a coderef you need to actually call it:
mysub( $arg ); # the usual way to call the sub
$coderef = \&mysub; # get the reference to the sub
$coderef->( $arg ); # call the coderef with an arg (or &$coderef($arg))
But to get something working with Test::Output you can wrap calls to the subroutines you want to test in an another subroutine:
use Test::Output;
sub callmysubwitharg { mysub($arg) }
stdout_like \&callmysubwitharg, qr/$expecting/, 'description';
And, this is doing the same thing using an anonymous subroutine:
stdout_like { mysub($arg) } qr/$expecting/, 'description';

How to test module functions which use hardcoded configuration file?

I want to make some tests on my modules.
Unfortunately, some functions in these modules use hardcoded configurations files.
package My::Module;
use strict;
use warnings;
use Readonly;
Readonly my $CONF_FILE => '/my/conf_file.xml';
=head1 FUNCTIONS
=head2 Info($appli)
Returns Application Information
=cut
sub Info
{
my $appli = shift;
my $conf = MyXML::Read($CONF_FILE);
foreach my $a (ARRAY($conf->{application}))
{
return ($a) if ($a->{name} eq $appli);
}
return (undef);
}
[some others functions that use this config file...]
The solution that came to my mind is to create a new function in each module that will change this default config file when I need it.
Then I will use that function in my tests...
Do you have any other (better ?) ideas ?
Well, the proper thing for me to tell you would be "don't use hard coded paths". It'll come back and bite you at some point in the future, I promise.
But... assuming you're resolved to using them, there are a number of ways to allow an override. You're right you could add a function that would let you change it, or you could use an environmental variable:
Readonly my $CONF_FILE => $ENV{'MY_CONF_FILE'} || '/foo/bar';
But the right thing to do is still to allow for other items to be passed in properly if you have a choice.

Can't locate object method via package subclassing DBI

this is my first foray into subclassing with perl and I am wondering why I am getting this simple error...
"Can't locate object method "prepare" via package "WebDB::st" at /home/dblibs/WebDB.pm line 19.". It seems to find the module WebDB ok, but not the prepare subroutine in ::st
First here's my package (both packages are in one file, WebDB.pm)
package WebDB;
use strict;
use DBI;
sub connect {
my $dbh = (DBI->connect ("DBI:mysql:test:127.0.0.1", "root","",
{ PrintError => 1, RaiseError => 0 }));
return bless $dbh, 'WebDB::st';
}
package WebDB::st;
our #ISA = qw(::st);
sub prepare {
my ($self, $str, #args) = #_;
$self->SUPER::prepare("/* userid:$ENV{USER} */ $str", #args);
}
1;
I also tried replacing the "our #ISA = qw(;;st)" with "use base 'WebDB'" and same problem.
I'm thinking it's probably something very simple that I'm overlooking. Many thanks! Jane
Subclassing DBI has to be done just right to work correctly. Read Subclassing the DBI carefully and properly set RootClass (or explicitly call connect on your root class with #ISA set to DBI). Make sure you have WebDB::st subclassing DBI::st and a WebDB::db class subclassing DBI::db (even if there are no methods being overridden). No need to rebless.
Avoid using base; it has some unfortunate behavior that has led to its deprecation, particularly when used with classes that are not in a file of their own.
Either explicitly set #ISA or use the newer parent pragma:
package WebDB;
use parent 'DBI';
...
package WebDB::db;
use parent -norequire => 'DBI::db';
...
package WebDB::st;
use parent -norequire => 'DBI::st';
...
Are WebDB and WebDB::st in one file or two? If they are in separate files, I don't see anything that is doing a use WebDB::st;, which would cause that file to be loaded.
You can do either of these things as a remedy -- put the two packages in the same file (that would look exactly as you have pasted it above), or add a use WebDB::st; line in WebDB.pm.
(I'd also add use strict; use warnings; in both these packages too.)
Also, the prepare function is not in ::st -- there is no such package (unless it is defined elsewhere). prepare is in the WebDB::st namespace -- via the package declaration. You are however declaring that WebDB::st has ::st as a parent.
If subclassing is as tricky as ysth seems to think, I might recommend Class::Delegator from CPAN. I use if for classes that want to act like IO. And by it, Perl is the first language (that I am aware of) that has an expression language for aggregation, delegation, encapsulation almost equal with inheritance.
package WebDB;
use strict;
use DBI;
use Class::Delegator
send => [ qw<connect ...> ]
, to => '{_dbihandle}'
...
;

Do Perl subclasses inherit imported modules and pragmas?

Lets say you have a parent Perl class in one file:
#!/usr/bin/perl
package Foo;
use strict;
use warnings;
use Data::Dumper;
sub new{
my $class = shift;
my %self = ();
return bless %self, $class;
}
1;
and a subclass in a different file:
#!/usr/bin/perl
package Bar;
use base "Foo";
1;
Will the subclass inherit the use statements from the parent? I know the method new will be inherited.
Basically I am trying to reduce the amount of boilerplate in my code and I can't find a clear answer to this question.
You asked in a comment about Test::Most and how it reduces boilerplate. Look at its import method. It's loading the modules into its namespace, adding those symbols to #EXPORT, then re-calling another import through a goto to finally get them into the calling namespace. It's some serious black magic that Curtis has going on there, although I wonder why he just didn't use something like import_to_level. Maybe there are some side effects I'm not thinking about.
I talk quite a bit about this sort of thing in Avoid accidently creating methods from module exports in The Effective Perler. It's in a different context but it's some of the same issues.
Here's a different example.
If some other module loads a module, you have access to it. It's not good to depend on that though. Here are three separate files:
Top.pm
use 5.010;
package Top;
use File::Spec;
sub announce { say "Hello from top!" }
1;
Bottom.pm
package Bottom;
use parent qw(Top);
sub catfiles { File::Spec->catfile( #_ ) }
1;
test.pl
use 5.010;
use Bottom;
say Bottom->catfiles( qw(foo bar baz) );
say File::Spec->catfile( qw( one two three ) );
I only load File::Spec in Top.pm. However, once loaded, I can use it anywhere in my Perl program. The output shows that I was able to "use" the module in other files even though I only loaded it in one:
Bottom/foo/bar/baz
one/two/three
For this to work, the part of the code that loads the module has to load before any other part of the code tries to use that module. As I said, it's a bad idea to depend on this: things break if the loading sequence changes or the loading module disappears.
If you want to import symbols, however, you have to explicitly load the module you want while you are in the package you want to import into. That's just so the exporting module defines the symbols in that package. It's not something that depends with scope.
Ah, good question!
Will the subclass inherit the use statements from the parent?
Well this depends on what you mean by inherit. I won't make any assumptions until the end, but the answer is maybe. You see, perl mixes the ideas of Classes, and Namespaces -- a package is a term that can describe either of them. Now the issue is the statement use all it does is force a package inclusion, and call the targets import() sub. This means it essentially has unlimited control over your package - and by way of that your class.
Now, compound this with all methods in perl being nothing more than subs that take $self as a first argument by convention and you're left with perl5. This has an enormous upside for those that know how to use it. While strict is a lexical pragma, what about Moose?
package BigMooseUser;
use Moose;
package BabyMooseUser;
our #ISA = 'BigMooseUser';
package Foo;
my $b = BabyMooseUser->new;
print $b->meta->name;
Now, where did BabyMooseUser get the constructor (new) from? Where did it get the meta class from? All of this is provided from a single use Moose; in the parent class (namespace). So
Will the subclass inherit the use statements from the parent?
Well, here, in our example, if the effects of the use statement are to add methods, than certainly.
This subject is kind of deep, and it depends if you're talking about pragmas, or more obscure object frameworks, or procedural modules. If you want to mitigate a parents namespace from affecting your own in the OO paradigm see namespace::autoclean.
For boilerplate reduction, I have a couple of strategies: Most of my classes are Moose classes, which takes care of OO setup and also gives me strict and warnings. If I want to have functions available in many packages, I'll create a project specific MyProject::Util module that uses Sub-Exporter to provide me with my own functions and my own interface. This makes it more consistent, and if I decide to change the Dumper (for example) later for whatever reason, I don't have to change lots of code. That'll also allow you to group exports. A class then usually looks something like this:
package Foo;
use Moose;
use MyProject::Util qw( :parsing :logging );
use namespace::autoclean;
# class implementation goes here
1;
If there's other things you regard as boilerplate and want to make simpler to include, it of course depends on what those things are.
A pragmatic answer to your problem: Either use, or look at how Modern::Perl does it to enforce strict and warnings.
You can get a definitive answer by examining the symbol tables for each package:
# examine-symbol-tables.pl
use Bar;
%parent_names = map{$_ => 1} keys %Foo::;
%child_names = map{$_ => 1} keys %Bar::;
delete $parent_names{$_} && ($common_names{$_} = delete $child_names{$_}) foreach keys %child_names;
print "Common names in symbol tables:\n";
print "#{[keys %common_names]}\n\n";
print "Unique names in Bar symbol table:\n";
print "#{[keys %child_names]}\n\n";
print "Unique names in Foo symbol table:\n";
print "#{[keys %parent_names]}\n\n";
$ perl inherit.pl
Common names in symbol tables:
BEGIN
Unique names in Bar symbol table:
ISA isa import
Unique names in Foo symbol table:
Dumper new VERSION