NAME
Test::MethodFixtures - Convenient mocking of externalities by recording
and replaying method calls.
SYNOPSIS
Setting up mocked methods in a test script:
# my-test-script.t
...
use Test::MethodFixtures;
Test::MethodFixtures->new->mock("Their::Package::Method");
use My::Package; # has dependency on Their::Package::Method
... # unit tests for My::Package here
Example workflow using the mocked methods:
$> TEST_MF_MODE=record prove -l my-test-script.t
$> git add t/.methodfixtures
$> git commit -m 'methodfixtures for my-test-script.t'
$> prove -l my-test-script.t # uses saved data
More configuration options:
use Test::MethodFixtures
# optionally specify global arguments
mode => 'record',
storage => ...
;
my $mocker = Test::MethodFixtures->new(
mode => 'record', # set locally for this object
# optionally specify alternative storage:
# override default storage directory
dir => '/path/to/storage',
# use Test::MethodFixtures::Storage::File class (default)
storage => 'File',
# use alternative Test::MethodFixtures::Storage object
storage => $storage_obj,
# load alternative Test::MethodFixtures::Storage class
storage => '+Alt::Storage::Class',
# or:
storage => { '+Alt::Storage::Class' => \%options },
# without '+' prefix, 'Test::MethodFixtures::Storage' is prepended to name
);
# simple functions and class methods - can store all arguments
$mocker->mock("Their::Package::Method");
# object methods - we need to turn $_[0] ($self) into an
# unique identifier for object, not memory reference
$mocker->mock( "Their::Object::Method",
sub { $_[0]->firstname . '-' . $_[0]->lastname } );
# do the same for other arguments
$mocker->mock(
"Their::Object::Method",
sub {
( $_[0], # use as-is
$_[1]->firstname . '-' . $_[1]->lastname, # object in $_[1]
# no need to list further arguments if no more changes required
);
}
);
# skipping arguments that shouldn't be saved - set to undef
$mocker->mock(
"Their::Package::Method",
sub {
( $_[0], # keep
undef, # discard
# further args kept as-is
);
}
);
BREAKING CHANGE
v0.07 - the default file storage now uses hyphens instead of colons as
package name separators.
DESCRIPTION
Record and playback method arguments, for convenient mocking in tests.
With this module it is possible to easily replace an expensive,
external or non-repeatable call, so that there is no need to make that
call again during subsequent testing.
This module aims to be low-dependency to minimise disruption with
legacy codebases. By default it tries to use
Test::MethodFixtures::Storage::File to record method data. Other
storage classes can be provided instead, to use modules available to
your system.
N.B. This module should be considered ALPHA quality and liable to
change.
Despite not providing any test methods, it is under the Test::
namespace to aid discovery and because it makes little sense outside of
a test environment. The name is inspired by database 'fixtures'.
Feedback welcome!
ATTRIBUTES
mode
Valid modes are record, playback, auto, and passthrough. See mock().
storage
A Test::MethodFixtures::Storage object.
METHODS
new
my $mocker = Test::MethodFixtures->new(
{ mode => 'record', # override global / ENV
dir => '/path/to/storage', # override default storage directory
# or use alternative Test::MethodFixtures::Storage object
storage => $storage_obj,
# or load alternative Test::MethodFixtures::Storage:: class
storage => { 'Alt::Storage::Class' => \%options },
}
);
Class method. Constructor
mock
$mocker->mock("Their::Package::method");
$mocker->mock( "Their::Package::method", sub { ( $_[0], ... ) } );
In record mode mock() stores the return values of the named method
against the arguments passed through to generate those return values.
In playback mode mock() retrieves stored return values of the named
method for the arguments passed in.
In auto mode mock() either stores or retrieves values, depending on
their presence in the saved data. This mode is useful for adding tests
without the need to re-record the other calls.
In passthrough mode the arguments and return values are passed to and
from the method as normal (i.e. turns off mocking).
The arguments passed to the mocked method are used to create the key to
store the results against.
Optionally mock() takes a second argument of a coderef to manipulate
@_, for example to prevent storage of a non-consistent value or to
stringify an object to a unique identifier.
store
Internal method. Wraps store() on storage object, and adds package
versions for this class and the storage class for comparison on
retrieval.
retrieve
Internal method. Wraps retrieve() on storage object, and checks package
versions are compatible.
BEHAVIOUR
* Warns if the module versions used to create the saved data is more
recent than those currently running.
* Handles calling context (list or scalar). Satisfies code using
wantarray.
RATIONALE
Testing is good, but also hard to do well, especially with complex
systems. This module aims to provide a simple way to help isolate code
for testing, and get closer to true "unit testing".
Why not mock objects?
Mock objects are a good way to satisfy simple dependencies, but have
many drawbacks, especially in complex systems:
* They require writing of more code (more development time and more
chances for bugs). The mocking code may end up being a duplication of
existing behaviour of the mocked code.
* They have to be kept up-to-date with the code that they are
mocking, yet are not usually stored with that code or maintained by
the same developers. Besides the extra development costs, divergence
may only be noticed later and so the tests are of less value.
Further reading
*
https://www.destroyallsoftware.com/blog/2014/test-isolation-is-about-avoiding-mocks
SEE ALSO
* LWP::UserAgent::Mockable
* Test::Mimic
SUPPORT
Bugs / Feature Requests
Please report any bugs or feature requests through the issue tracker at
https://github.com/mjemmeson/Test-MethodFixtures/issues. You will be
notified automatically of any progress on your issue.
Source Code
This is open source software. The code repository is available for
public review and contribution under the terms of the license.
https://github.com/mjemmeson/Test-MethodFixtures
git clone git://github.com/mjemmeson/Test-MethodFixtures.git
AUTHOR
Michael Jemmeson
COPYRIGHT
Copyright 2015- Michael Jemmeson
LICENSE
This library is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.