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
                );
            }
        );

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 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!

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 stores the return values of the named method against the
    arguments passed through to generate those return values.

    In playback mode retrieves stored return values of the named method for
    the arguments passed in.

    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 <mjemmeson@cpan.org>

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.

