Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

[Solved] Parameter injection for testing

by davies (Prior)
on Aug 24, 2022 at 14:43 UTC ( [id://11146368]=perlquestion: print w/replies, xml ) Need Help??

davies has asked for the wisdom of the Perl Monks concerning the following question:

I am trying to write a modulino that will accept various parameters through GetOpt::Long. I want to write unit tests for subs that will behave differently depending on the parameters. A SSCCE modulino and test file demonstrate the problem. The modulino:

package mdlno; use strict; use warnings; use feature 'say'; my $param = shift; say main() unless caller(); sub main { $param //= 'World'; return 'Hello ' . $param; }

Tests:

use strict; use warnings; use Test::More tests => 2; use FindBin qw( $RealBin ); use lib $RealBin; use mdlno; my $rtn = mdlno->main(); is $rtn, 'Hello World', 'Works without parameter'; $mdlno::param = 'and goodbye'; $rtn = mdlno->main(); is $rtn, 'Hello and goodbye', 'Works with parameter';

The first test passes but the second fails. I have tried all sorts of variations including Exporter and local, but using the debugger tells me every time that the address used in the test code differs from the address of the parameter in the modulino. All clues gratefully received.

Regards,

John Davies

Replies are listed 'Best First'.
Re: Parameter injection for testing
by Corion (Patriarch) on Aug 24, 2022 at 15:02 UTC
    my $param = shift;

    $param is a lexical variable so you have no way of getting at it. Make it a global variable and you can get at it from the outside:

    our $param = shift;

    ... or even better, consider initializing the parameters from @ARGV, and doing so only in main, or passing @ARGV explicitly to main:

    say main(@ARGV) unless caller(); sub main { my( @args ) = @_; GetOptionsFromArray( \@args, ... ); $param //= 'World'; return 'Hello ' . $param; }

      Thank you. I had tried something using our, but it was when I was floundering and obviously I did it wrong. The modulino will have lots of optional arguments and passing them all by name would be a pain. I may look at passing parameters when the modulino is closer to completion, but I am a fan of Getopt::Long, which does lots of things I like.

      Regards,

      John Davies

Re: Parameter injection for testing
by LanX (Saint) on Aug 24, 2022 at 19:32 UTC
    If you want to keep my $param private in file scope - instead of switching to our - then you need a setter function in that scope closing over $param

    sub set_param { $param = shift }

    A generic solution for various vars could be done with PadWalker or evals.

    But I'd rather not see that compiled into production code.

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery

      > A generic solution for various vars could be done with PadWalker or evals.

      here a little demo using eval

      NB: the allowed vars must be bound inside the setter sub, otherwise the eval will fail.

      use v5.12; use warnings; { package TEST; my $param = 666; sub getset { my %args = @_; return [$param] # list of allowed vars unless @_; while ( my ( $var, $val ) = each %args ) { eval "$var = $val"; } } sub show { say $param; } } package main; TEST::show(); TEST::getset( '$param' => 42 ); TEST::show();

      666 42

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery

Re: Parameter injection for testing
by Anonymous Monk on Aug 25, 2022 at 10:41 UTC

      This won't work if somebody invokes the module via a symlink or something like that...

      The point (for me) of using a modulino is that the code can be invoked from the command line, but it can be used by a test script and unit tests run. Have a look at Szabgab's post https://perlmaven.com/modulino-both-script-and-module. I have struggled with unit tests for command line scripts in the past & have found modulinos a very elegant solution.

      Regards,

      John Davies

        I'm using modulinos differently (at least I think so)

        If the module is called directly prove is executed.

        Like this I can hit F5 (run) in editor buffers holding the module for the whole suite or on individual test files for them only.

        Like with Perl::Tidy and perltidy.pl, I rather prefer one (or more) separate script.pl instead of adding that functionality into the module.

        And the scripts are also executed with the same F5 key.

        Some prefer an extra key/command to run the test suite but this requires individual configuration of the IDE or the "project file".

        I rather prefer holding the current prove config as perl code in the modules while developing.

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11146368]
Approved by johngg
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others avoiding work at the Monastery: (1)
As of 2024-04-19 00:43 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found