[ANNOUNCE] Test::MockRandom 0.92
From: David Golden (dg-junk_at_hyperbolic.net)
Date: 12/23/04
- Previous message: Brian McCauley: "Re: compile problems with XML::LibXML"
- Next in thread: Terrence Brannon: "Re: [ANNOUNCE] Test::MockRandom 0.92"
- Reply: Terrence Brannon: "Re: [ANNOUNCE] Test::MockRandom 0.92"
- Reply: David Golden: "Re: [ANNOUNCE] Test::MockRandom 0.92"
- Reply: Terrence Brannon: "Re: [ANNOUNCE] Test::MockRandom 0.92"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Date: Thu, 23 Dec 2004 06:49:28 -0600
NAME
Test::MockRandom - Replaces random number generation with non-random
number generation
SYNOPSIS
# functional
use Test::MockRandom;
srand(0.5);
if ( rand() == 0.5 ) { print "good guess!" };
# object-oriented
use Test::MockRandom ();
my $nrng = Test::MockRandom->new(0.42);
$nrng->rand(); # returns 0.42
# override rand in another package
use Test::MockRandom 'Some::Other::Package';
use Some::Other::Package; # contains sub foo { return rand }
srand(0.13);
Some::Other::Package::foo; # returns 0.13
# using a seed list and "oneish"
srand(0.23, 0.34, oneish() );
rand(); # returns 0.23
rand(); # returns 0.34
rand(); # returns a number just barely less than one
rand(); # returns 0, as the seed array is empty
DESCRIPTION
This perhaps ridiculous-seeming module was created to test routines that
manipulate random numbers by providing a known output from "rand". Given
a list of seeds with "srand", it will return each in turn. After seeded
random numbers are exhausted, it will always return 0. Seed numbers must
be of a form that meets the expected output from "rand" as called with
no arguments -- i.e. they must be between 0 (inclusive) and 1
(exclusive). In order to facilitate generating and testing a nearly-one
number, this module exports the function "oneish", which returns a
number just fractionally less than one.
Depending on how this module is called with "use", it will export "rand"
either to the current package or to another specified package (e.g. a
class being tested) or even globally. This module also includes the
function "export_rand_to" which can be used to explictly override rand
in another package after "use" has been called. See "USAGE" for details.
Alternatively, this module can be used to generate objects, with each
object maintaining its own distinct seed array.
USAGE
Overriding "rand" in the current package
To override "rand" in the current package, simply "use" the module as
normal.
use Test::MockRandom;
This imports "rand" and "srand" into the current namespace, masking any
such calls from reaching the built-in functions. It also imports
"oneish", and "export_rand_to".
Overriding "rand" in a different package with "use"
There are two ways to override "rand" in different package. The simplest
is to provide the name(s) of the package to be overridden in the "use"
statement. This will export "rand" to the listed packages and will
export "srand", "oneish", and "export_rand_to" to the current package.
You must "use" Test::MockRandom before you "use" the target package.
This is a typical case for testing a module that uses random numbers:
use Test::More;
use Test::MockRandom qw( Some::Package );
BEGIN { use_ok( Some::Package ) }
srand(0.5)
# assume sub foo { return rand } in Some::Package
Some::Package::foo() # returns 0.5
If you wish to export "rand" to both another package and the current
package, simply include the current package in the list provided to
"use". All of the following idioms work.
use Test::MockRandom qw( main Some::Package );
use Test::MockRandom __PACKAGE__, 'Some::Package';
# The following doesn't interpolate __PACKAGE__ as above, but
# Test::MockRandom will still DWIM and handle it correctly
use Test::MockRandom qw( __PACKAGE__ Some::Package );
Overriding "rand" in a different package explicitly with
"export_rand_to"
In order to override the built-in "rand" in another package,
Test::MockRandom must export its own "rand" function before the target
package is compiled. The simple approach (described above) of providing
the target package in the "use Test::MockRandom" statement accomplishes
this because "use" is equivalent to a "require" and "import" within a
"BEGIN" block. To explicitly override "rand" in another package, you can
also call "export_rand_to", but it must be enclosed in a "BEGIN" block
of its own:
use Test::MockRandom;
BEGIN { Test::MockRandom::export_rand_to( 'AnotherPackage' ); }
use AnotherPackage;
This "BEGIN" block must not include a "use" statement for the package to
be overridden, or perl will compile the package to be overridden before
the "export_rand_to" function has a chance to execute and override the
system "rand". This is very important in testing. The "export_rand_to"
call must be in a separate "BEGIN" block from a "use_ok" test, which
should be enclosed in a "BEGIN" block of its own:
use Test::MockRandom;
BEGIN { Test::MockRandom::export_rand_to( 'AnotherPackage' ); }
BEGIN { use_ok( 'AnotherPackage' ); }
Given these cautions, it's probably best to use the simple approach with
"use", which does the right thing in most circumstances.
Overriding "rand" globally
This is just like overriding "rand" in a package, except that you
override it in "CORE::GLOBAL".
use Test::MockRandom 'CORE::GLOBAL';
# or
BEGIN { Test::MockRandom::export_rand_to('CORE::GLOBAL') }
You can always access the real built-in "rand" by calling it explicitly
as "CORE::rand".
Overriding "rand" in a package that also contains a "rand" function
This is tricky as the order in which the symbol table is manipulated
will lead to very different results. This can be done safely (maybe) if
the module uses the same rand syntax/prototype as the system call. In
this case, you will need to do an explicit override (as above) but do it
after importing the package. I.e.:
use Test::MockRandom;
use SomeRandPackage;
BEGIN { Test::MockRandom::export_rand_to('SomeRandPackage');
The first line is mostly to get the right exporting of auxilliary
function to the current package. The second line will define a "sub
rand" in "SomeRandPackage", overriding the results of the first line.
The third line then re-overrides the "rand". You may see warnings about
"rand" being redefined.
Depending on how your "rand" is written and used, there is a good
likelihood that this isn't going to do what you're expecting, no matter
what. If your package that defines "rand" relies upon the system
"CORE::GLOBAL::rand", then you may be best off overriding that instead.
FUNCTIONS
"new"
$obj = new( LIST OF SEEDS );
Returns a new Test::MockRandom object with the specified list of seeds.
"srand"
srand( LIST OF SEEDS );
$obj->srand( LIST OF SEEDS);
If called as a bare function call or package method, sets the seed list
for bare/package calls to "rand". If called as an object method, sets
the seed list for that object.
"rand"
$rv = rand();
$rv = $obj->rand();
$rv = rand(3);
If called as a bare or package function, returns the next value from the
package seed list. If called as an object method, returns the next value
from the object seed list.
If "rand" is called with a numeric argument, it follows the same
behavior as the built-in function -- it multiplies the argument with the
next value from the seed array (resulting in a random fractional value
between 0 and the argument, just like the built-in). If the argument is
0, undef, or non-numeric, it is treated as if the argument is 1.
Using this with an argument in testing may be complicated, as limits in
floating point precision mean that direct numeric comparisons are not
reliable. E.g.
srand(1/3);
rand(3); # does this return 1.0 or .999999999 etc.
"oneish"
srand( oneish() );
if ( rand() == oneish() ) { print "It's almost one." };
A utility function to return a nearly-one value. Equal to ( 2^32 - 1 ) /
2^32. Useful in "srand" and test functions.
"export_rand_to"
export_rand_to( 'Some::Other::Package' );
This function exports "rand" into another package namespace. This is
useful in testing object which call "rand". E.g.,
package Some::Class;
sub foo { print rand(); }
package main;
use Test::MockRandom;
export_rand_to( 'Some::Class' );
srand(0.5);
Some::Class::foo(); # prints "0.5"
Note that this uses the Test::MockRandom package globals, not class
objects. So a call to "srand" in the main package still affects the
results of "rand" called in "Some::Class".
The effect of this function is highly dependent on when it is called in
the compile cycle. See "USAGE" for important details and warnings.
BUGS
Please report bugs using the CPAN Request Tracker at
http://rt.cpan.org/NoAuth/Bugs.html?Dist=Test-MockRandom
AUTHOR
David A. Golden (DAGOLDEN)
dagolden@dagolden.com
http://dagolden.com/
COPYRIGHT
Copyright (c) 2004 by David A. Golden
This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
The full text of the license can be found in the LICENSE file included
with this module.
SEE ALSO
Test::MockObject
Test::MockModule
- Previous message: Brian McCauley: "Re: compile problems with XML::LibXML"
- Next in thread: Terrence Brannon: "Re: [ANNOUNCE] Test::MockRandom 0.92"
- Reply: Terrence Brannon: "Re: [ANNOUNCE] Test::MockRandom 0.92"
- Reply: David Golden: "Re: [ANNOUNCE] Test::MockRandom 0.92"
- Reply: Terrence Brannon: "Re: [ANNOUNCE] Test::MockRandom 0.92"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Relevant Pages
|