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

I've created a module with three exported subroutines and around 25 private subroutines. I'm now trying to write tests for both the exported and private subs. I can test for the exported subroutines just fine. I can test the private subroutines as long as I correctly use the fully qualified name. The problem is that I have to explicitly qualify the full module package and subroutine. What I want to do is put the package name(only) in a variable and then test the fully qualified package name using the variable followed by ::subroutine.

I think it's a simple concatenation problem, but I can't get it right.

In the tests below, 1, 2, 3, and 4 give the expected behavior.
However tests 5, 6, and 7 give me false positives.
I am interpreting the false positives as  defined is only looking to see whether a value exists, which it does. However, &Mysimple_mod::bogus does not exist. I'm aware of using not_ok, but for this question I'm not trying to test for non-existent subroutines, I want to accurately make sure it is there. Later on in my journey I plan to write tests that actually use the subroutines. You'll see a few of my failed attempts in the .t file below.

The results:

1..7 ok 1 - use Mysimple_mod; ok 2 - \#2 external is defined ok 3 - \#3 internal is defined not ok 4 - \#4 bogus subroutine # Failed test '\#4 bogus subroutine' # in test_Mysimple_mod.t at line 11. ok 5 - \#5 bogus subroutine ok 6 - \#6 bogus subroutine &Mysimple_mod::bogus ok 7 - \#7 bogus subroutine # Looks like you failed 1 test of 7.
Here's my test file:
use strict; use warnings; use Test::More tests => 7; my $val = '&Mysimple_mod'; BEGIN { use_ok('Mysimple_mod', qw (external) ) }; ok ( defined( &external ) , "#2 external is defined"); ok ( defined( &Mysimple_mod::internal ) , "#3 internal is defined"); ok ( defined( &Mysimple_mod::bogus ) , "#4 bogus subroutine"); ok ( defined( "${val}".'::bogus' ) , "#5 bogus subroutine"); ok ( defined( ${val}.'::bogus' ) , "#6 bogus subroutine"); my $catval = "${val}" .'::bogus'; print "\n$catval\n"; ok ( defined( $catval ) , "#7 bogus subroutine");
My module:
#!/usr/bin/perl package Mysimple_mod; use strict; use warnings; require Exporter; our @ISA=qw(Exporter); our @EXPORT_OK = qw( external ); sub external { my $val = shift; my $return_val = internal($val); return $return_val; } sub internal { my $val = shift; my $calc = $val * 3; return $calc; } 1;
I'm frequently asked "what are you trying to do, what is your overall goal?"
The primary objective is to learn Perl and attempt to help out somehow, someplace so I can contribute back. The snippet of the module is just an example. The real module let's me take one of three exported subroutines and a)fetches an RPM file via FTP from a server to install on a remote machine and then installs it, or b)just fetches it for manual installation, or c) lists various things(help on the script, current RPM version installed, RPM packages already fetched and on the machine, OS name, etc. about the remote machine). I'm just getting started on the testing phase. Right now, it's easier just to run the module/script to test the functionality but I want to understand how to test.

Any and all feedback welcome. Thank-you.

Replies are listed 'Best First'.
Re: Concatenate strings before Test::More::ok
by zwon (Abbot) on Apr 19, 2009 at 20:19 UTC

    You're checking if string value defined. And it's certainly defined. Try the following:

    use strict; use warnings; use Test::More tests => 7; my $val = 'Mysimple_mod'; BEGIN { use_ok('Mysimple_mod', qw (external) ) }; ok ( defined( &external ) , "#2 external is defined"); ok ( defined( &Mysimple_mod::internal ) , "#3 internal is defined"); ok ( defined( &Mysimple_mod::bogus ) , "#4 bogus subroutine"); ok ( defined( &{$val.'::bogus'} ) , "#5 bogus subroutine"); ok ( defined( &{$val.'::internal'} ) , "#6 internal subroutine" +); my $catval = "${val}" .'::bogus'; print "\n$catval\n"; ok ( defined( &$catval ) , "#7 bogus subroutine");
      Thanks almut and zwon. The solution works perfectly. Thank-you because I don't think I would have thought to try that though it makes sense after seeing it.
Re: Concatenate strings before Test::More::ok
by almut (Canon) on Apr 19, 2009 at 20:19 UTC

    You need an eval, otherwise your concatenated '&Mysimple_mod::bogus' is just a string, not a function.

    ok ( defined( eval $val.'::bogus' ) , "#6 bogus subroutine");

    Update: sorry, spoke too soon. The problem with the eval is that it would actually call the routine...  See zwon's reply for a working solution.

Re: Concatenate strings before Test::More::ok
by AnomalousMonk (Archbishop) on Apr 20, 2009 at 00:34 UTC
    What you're trying to do is use 'symbolic' (or 'soft') references, which is officially Frowned Upon (and for good reason), not to mention precluded by the  use strict 'refs'; stricture (one of the strictures that  use strict; turns on). See discussion at Symbolic references in perlref, search PM for more comments. This stricture is turned off by the  no strict 'refs'; pragma. See strict.

    What you may want to do is something like this (I'm using your original module):

    use warnings; use strict; use Test::More 'no_plan'; BEGIN { use_ok('Mysimple_mod', qw (external) ) }; my $mod = 'Mysimple_mod'; ok ( defined( &external ), "#2 external is defined"); ok ( defined( &Mysimple_mod::internal ), "#3 internal is defined"); ok ( defined( &Mysimple_mod::bogus ), "#4 bogus subroutine"); ok ( defined( &{"${mod}::bogus"} ), "#5 bogus subroutine"); my @softies = qw(internal); # my @softies = qw(internal bogus); for my $softy (@softies) { my $func = "${mod}::$softy"; no strict 'refs'; ok( $func->(3) == 9, qq{invoke soft ref'd func $func( 9)} ); ok( $func->(3) == 10, qq{invoke soft ref'd func $func(10)} ); }
    Output:
    C:\@Work\Perl\monks\gctaylor1>perl test_soft_refs_1.pl ok 1 - use Mysimple_mod; ok 2 - \#2 external is defined ok 3 - \#3 internal is defined not ok 4 - \#4 bogus subroutine # Failed test (test_soft_refs_1.pl at line 77) not ok 5 - \#5 bogus subroutine # Failed test (test_soft_refs_1.pl at line 78) ok 6 - invoke soft ref'd func Mysimple_mod::internal( 9) not ok 7 - invoke soft ref'd func Mysimple_mod::internal(10) # Failed test (test_soft_refs_1.pl at line 87) 1..7 # Looks like you failed 3 tests of 7.
    See what happens if you add  bogus to the  @softies array.
Re: Concatenate strings before Test::More::ok
by JavaFan (Canon) on Apr 20, 2009 at 22:26 UTC
    Alternatively, you could just run your tests from the package Mysimple_mod:
    package Mysimple_mod; ok ( defined( &external ), "..."); ok ( defined( &internal ), "..."); ... etc ...