Search code examples
unit-testingperlreadonlyreadonly-variable

How do I change a Perl Readonly scalar in a module for a unit test?


The only help I've found on the internet so far is this blog. Which I thought was going to get me there, but I don't think it's actually changing the values in my module. I made a sample to show what I mean.

package Module;

use 5.012;
use strict;
use warnings;
use Readonly   qw( );

use parent     qw(Exporter);
our @EXPORT_OK = qw(
   &GetReadonly
);
our %EXPORT_TAGS = (
   all => [ @EXPORT_OK ] );

Readonly::Scalar my $HOST => 'host.web.server.com';

sub GetReadonly
{
   return $HOST;
}

1;

And the test code:

#!perl

use strict;
use warnings;
use Test::More 'no_plan';
use Module qw/ :all /;

is($Module::HOST, 'host.web.server.com');     # HOST == undef

my $fake_host = 'fakemail.web.server.com';

{
   no warnings 'redefine';
   local *Readonly::Scalar::STORE = sub { ${$_[0]} = $_[1]; };
   $Module::HOST = $fake_host;
}

is(GetReadonly(), $fake_host);      # Returns host.web.server.com

If I use the Module::HOST from the blog, I get a bareword compile error.

Is there some better way to mock a Readonly for a unit test?


Solution

  • The blog was probably written in the old days when Readonly was implemented in pure Perl using tie. Nowadays, Readonly is implemented using XS. To make a variable not readonly anymore, you can call

    Internals::SvREADONLY( $Module::HOST, 0 );
    

    To be able to access the variable from outside of the module, it must be declared our, not my (as the blog correctly shows).

    But the main question is: why do you need to test a different value in the variable, if the variable isn't supposed to be writable?