Test::CallFlow - trivial planning of sub call flows for fast unit test writing.
Version 0.03
Mock packages for planning expected interactions in tests:
use Test::CallFlow qw(:all); my $mocked = mock_object( 'My::Mocked::Package::Name' ); $mocked->my_method( arg_any(0,9) )->result( 'return value' ); mock_run(); die "test did not return right value" if $mocked->my_method( 'any', 'arguments' ) ne 'return value'; mock_end();
Test::CallFlow functions are used here in a procedural manner because straightforward test scripts are seen as primary use case. As well you may create objects with new() and use the provided functions as object methods.
Test::CallFlow
new()
use Test::More plan_tests => 1; use Test::CallFlow qw(:all); # just mock a package mock_package( 'Just::Mocked' ); # mock a package and make an object of it my $mocked = mock_object( 'My::Mocked::Package::Name', # must specify package name { 'optional' => 'content' } ); # may specify what to bless
Just::Mocked->new() # no arguments ->result( $mocked ); # return the mock object my $get_call = # refer to this Test::CallFlow::Call object $mocked->get( "FieldX" ) # one equal string argument ->result( 1, 2, 3 ) # return array ( 1, 2, 3 ) on first call ->result( 4, 5, 6 ) # return array ( 4, 5, 6 ) on second call ->result( 7, 8, 9 ) # return array ( 7, 8, 9 ) on any subsequent calls ->min(0) # this call is optional ->max(9) # this call can be made at most 9 times ->anytime; # may be called at this step or any time later $mocked->set( arg_check( qr/^Field/ ), # first argument matching regular expression arg_any( 1, 99 ) ); # 1-99 arguments with any values # return nothing (undef or empty array) $mocked->save( arg_check( \&ok_file ) ) # use own code to check argument ->end( $get_call ); # end scope: $get_call can be made no more # if you wish to use parts of the real package unmocked as is, # load it after planning but before running: use My::Mocked::Package::Name; # remember that nothing keeps you from still just adding your own: package My::Mocked::Package::Name; sub really_customized {} # skipping mock system package main; # remember to end your own package definition
mock_run(); # flow of calls from test planned, now prepare to run the test(s) eval { # package was already declared as loaded at mock_run() # so code under test may freely try to 'use' it use My::Mocked::Package::Name; code_under_test(); # dies on any unplanned call to a mocked package or sub mock_end(); # dies if any expected calls were not made and reports them }; is( $@, '', "code_under_test() executed according to prepared plan" ); mock_clear(); # flush state, plan and mocks so you may plan another test call flow
To make it easier to start refactoring existing complicated legacy code, Test::CallFlow also provides preliminary sub call recording functionality:
# load the packages used by code under test first use My::Mocked::Package::Name; use Other::Mocked::Package; # then declare them for mocking; this saves the original subs aside mock_package( 'My::Mocked::Package::Name', 'Other::Mocked::Package' ); # start recording record_calls_from( 'Package::Under::Test' ); # now calls to mocked packages will be made and recorded with their args and results use Package::Under::Test; Package::Under::Test->code_under_test(); # generate code to serve as basis for your test run print join ";\n", map { $_->name() } mock_plan()->list_calls();
Test::CallFlow is actually object-oriented; default instance creation is hidden. Usability of multiple simultaneous mock objects is hindered by Perl global package namespace. Only one object may be used for recording, planning or running at a time. A separate object can be used for each of those tasks simultaneously as long as they don't mock same packages. Just do one thing at a time and mock_clear() straight after to steer clear of any problems.
mock_clear()
use Test::CallFlow; my $flow = Test::CallFlow->new( autoload_template => '' # do not declare AUTOLOAD, use explicit mock_call()s only ); $flow->mock_package( 'Just::Mocked' ); $flow->mock_call( 'Just::Mocked::new', 'Just::Mocked' )->result( bless( {}, 'Just::Mocked' ) ); $flow->mock_run; print Just::Mocked->new; $flow->mock_end;
Map of state names to state IDs. Used to refer to flow object states:
unknown, record, plan, execute, failed, succeeded.
List of state names. Used to get printable name for state IDs.
Contains default values for instance properties.
Array of created instances. Used by mocked methods to locate the related instance responsible of building and following the plan, ie. checking the call and providing right result to return.
Default properties are defined in %Test::CallFlow::prototype. They may be specified as parameters for new or environment variables with prefix mock_, such as mock_save.
%Test::CallFlow::prototype
new
mock_
mock_save
Template texts below may contain #{variablename} placeholders that will be replaced by context-specific or Test::CallFlow object property values.
#{variablename}
These may be useful for heavier customizations, although it'll probably be easier to just define more hairy mock package parts straight in the test script.
Template text for mock package definitions. See code for contents.
#{packagename}
#{subs}
Template for code to put into mocked subs.
#{subname}
Template for code to put into mocked AUTOLOAD subs.
Template for package definition at mock_run.
mock_run
Default value contains redefinition warning suppression and expects #{packagebody} variable to contain actual mock package definition.
#{packagebody}
These are set and used at planning and runtime.
One of %Test::CallFlow::state values.
%Test::CallFlow::state
Default is plan. mock_run() sets state to execute. mock_end sets it to succeeded - or failed if more calls were expected. Failure in a mock call sets it to failed. mock_clear and mock_reset unconditionally set it back to plan.
plan
mock_run()
execute
mock_end
succeeded
failed
mock_clear
mock_reset
Index of this object in @Test::CallFlow::instances.
@Test::CallFlow::instances
Contains data about packages and subs to mock gathered from calls in planning mode.
Call execution plan as a Test::CallFlow::Plan object containing Test::CallFlow::Call objects.
Test::CallFlow::Plan
Test::CallFlow::Call
Hash of package names created by record_calls_from() for checking which calls to record during recording.
record_calls_from()
Controls debug information printing. Class names in this string cause debugging info to be printed from them. Options are: Mock, Plan, Call, ArgCheck. Derived from $ENV{DEBUG}.
Mock
Plan
Call
ArgCheck
$ENV{DEBUG}
Controls whether to print debug info in this class.
Sometimes it might be nice to put the files into a temporary directory included in @INC, or to keep them around for debugging or faster loading later.
Whether to save package definitions into files. Default is not to save.
If set at construction, the temporary directory will be prepended to @INC so that the mocks will load with use hiding any real implementations.
use
Base directory for saving packages. Default is system temporary directory.
Template for name of subdirectory inside basedir to contain saved package file hierarchy. Default is 'perl-mock-<process-id>-<mock-instance-number>'.
$mocker = Test::CallFlow::instance;
Returns the first instance of this class created with given properties. Creates one if there isn't.
This is called from each of the mock_ subs exported with :all tag so that the library can easily be used procedurally.
:all
my $mocker = Test::CallFlow->new( %properties );
Returns a new Test::CallFlow object with given properties. Properties not given are taken from %Test::CallFlow::prototype.
record_calls_from( 'Package::Under::Test', 'Supplementary::Package::Under::Same::Test', );
Starts recording calls from specified packages.
Returns self.
mock_run;
End planning mocked calls and start executing tests.
If compilation of a package fails, confesses its whole source.
mock_end;
End test execution.
If any expected calls have not been made, dies with a list of unsatisfied calls.
mock_clear;
Clears plan. Restores any original subs covered by mocks. Resets state unconditionally back to planning.
Does not touch any other properties of mocked packages than subs mocked with mock_sub() (that's used implicitly during normal planning or recording).
mock_sub()
Does not currenctly remove any files created by requesting packages to be saved. Maybe that should some day be a configurable option.
mock_reset;
Reset mock plan for re-run.
mock_package( 'Package::Name' );
Declares package of given name to be mocked. Returns nothing. Dies if the package declaration fails - ie. when invalid templates were specified for this mock object.
AUTOLOAD method gets declared to enable building plan by mock calls.
AUTOLOAD
my $mocked = mock_object( 'Package::Name' ); my $mocked_scalar = mock_object( 'Scalar::Blessed', "bless this scalar" );
Returns an object of given mocked package. Declares that package for mocking if necessary.
my $props_ref = mock_sub( 'Package::Name', 'sub_name', 'sub #{subname} { warn "#{subname}(@_) called" }' );
Declares given package to contain given sub such that it will actually execute Test::CallFlow::mock_call - or alternatively given template text.
Template may contain placeholders marked as #{name} to be substituted with values of any property of the Test::CallFlow object or
Name of sub being defined
Name of package being defined
mock_call( 'Mocked::Package::sub_name', @args );
Called from mocked packages.
During plan buildup, adds calls to mock call plan list.
During test execution, tries to find a planned mock call matching given call. Returns planned value. Dies on mismatch.
During recording calls the original method. If caller is a record candidate, records the call and result.
Returns reference to the Test::CallFlow::Plan object.
$mocked->method( arg_check(qr/../), arg_check( sub { $_[2]->[$_[1]] < 5 }, 0, 99 ) );
Instantiates an object of correct subclass of Test::CallFlow::ArgCheck for given test; either Regexp or Code reference.
Arguments are
$mocked->method( arg_any, 'X', arg_any( 0, -1 ) );
Returns an argument checker that passes any arguments. Optional arguments specify minimum (default 1) and maximum (default same as minimum) possible number of arguments to pass.
These are not exported with :all.
Saves given package if saving is not disabled for it and enabled for it or by default. Location is basedir/savedir/containingpackage/packagename.pm.
Dies on I/O failures.
my $package_definition = plan_mock_package( 'My::Mocked::Package::Name' );
Returns a string containing the perl code for a package with mock versions of all methods called so far.
my $text = $mocker->embed( 'sub #{subname} { "mocked sub of #{packagename}" }', subname => 'my_mock' );
Embeds given values and object properties as referred by placeholders in given text.
Does not recurse indefinitely, but gives silently up after 15 recursions.
my $filename = mock_package_filename( 'My::Mocked::Package::Name' );
Returns relative path and filename combination string for given package name.
$mocker->plan_mock_call( 'Mocked::Package::sub_name', @args );
Adds a call with given package::sub name and arguments to call plan.
Called from mock_call when running tests against plan.
mock_call
Returns result from planned mock call matching given executed call if one exists.
Called from mock_call when recording calls.
Returns result of call to original method.
MockCommand
Integration to cover external command calls.
Tied Variables
Provide easy methods for recording, restricting and testing data access.
Test::CallFlow::Package
Would allow for neat stuff like
mock_package( 'Bar' )->vars( ISA => [ 'Foo' ], VERSION => 0.01 );
ArgCheck::Hash
ArgChecker for deep structure comparison. Add also arg_deep.
arg_deep
ArgCheck::Array
ArgChecker for a match in a list; used as arg_check( \@in ).
arg_check( \@in )
Ref Checking
Document the fact that Regexp /^Type::Name=/ may be used for reference type checks.
Kalle Hallivuori, <kato at iki.fi>
<kato at iki.fi>
Please report any bugs or feature requests to bug-test-callflow at rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Test-CallFlow. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
bug-test-callflow at rt.cpan.org
You can find documentation for this module with the perldoc command.
perldoc Test::CallFlow
You can also look for information at:
RT: CPAN's request tracker
http://rt.cpan.org/NoAuth/Bugs.html?Dist=Test-CallFlow
AnnoCPAN: Annotated CPAN documentation
http://annocpan.org/dist/Test-CallFlow
CPAN Ratings
http://cpanratings.perl.org/d/Test-CallFlow
Search CPAN
http://search.cpan.org/dist/Test-CallFlow/
Test::CallFlow provides a very simple way to plan mocks. Other solutions are available, each with their strong points.
Test::MockClass
Very clearly named methods are used to create and control mocks. Supports explicit call order. Does not provide unified flexible argument checking. Call tracking can be disabled.
Test::MockObject
Collects calls made so that you can check them in your own code afterwards.
Test::MockModule
You provide the code for each mocked method separately. No flow checks. Original methods are remembered and can be restored later.
Test::MockCommand
Mock external commands that your program calls.
A structure of calls the code under test should make.
A single call that the code under test might make.
Test::CallFlow::ArgCheck
Checkers for arguments to mocked function calls.
Test::CallFlow::ArgCheck::Equals
Pass arguments that match given string or undef.
Test::CallFlow::ArgCheck::Code
Pass arguments that given method returns true for.
Test::CallFlow::ArgCheck::Regexp
Pass arguments that are defined and match given regexp.
Test::CallFlow::ArgCheck::Any
Pass any arguments.
chromatic, author of Test::MockObject
Perl namespace management details I got from his code.
Simon Flack, author of Test::MockModule
Copyright 2008 Kalle Hallivuori, all rights reserved.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
To install Test::CallFlow, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Test::CallFlow
CPAN shell
perl -MCPAN -e shell install Test::CallFlow
For more information on module installation, please visit the detailed CPAN module installation guide.