Test-Mimic
=======================

Test::Mimic provides automatic object and package mocking via recorded data.
See perldoc Test::Mimic after installation and below for usage information.

This should be considered an ALPHA release.

INSTALLATION

To install this module type the following:

   perl Makefile.PL
   make
   make test
   make install

DEPENDENCIES

This module requires these other modules and libraries:

    Test::Mimic::Library
    Test::Mimic::Generator
    Test::Mimic::Recorder

DETAILS

Why?

You are making unit tests for code that has dependencies which are unpredictable,
unreliable or error prone. By using Test::Mimic to mock out these dependencies
you can focus on testing your code and not somebody else's.

Using Test::Mimic

High Level

You insert a small amount of Test::Mimic code into your testing code. Then you
insure that the unreliable dependency is temporarily in a good state. Next you
simply run your tests. Test::Mimic will record the behavior of the dependency.
Every future run of your tests will use the recorded information instead of the
actual dependency. The dependency has been mimicked!

The Details

Of course, it's not really that simple. Let's break this down a little more.
Test::Mimic works at the package level, so first determine which packages you
would like to mimic. Then insert the following into your testing code, perhaps a .t file,
before the packages to mimic could possibly be loaded (even indirectly).

   use Test::Mimic {
       'packages' => {
           'Package::A' => {},
           'Package::B' => {},
           'Package::C' => {},
       },
   };

Now run your tests. If they behave differently than without Test::Mimic or
break entirely consider:

    Could there be any strange compile time interactions between the packages?

        Note: Test::Mimic will require each package in no particular order.
        import will not be called.

    Do other packages need to be loaded before any of the packages to
    mimic are loaded?

Try to place the use Test::Mimic statement so that the above issues are minimized.
Mimic only what is absolutely necessary for the same reason. Also, this may not
be your fault. Test::Mimic does some (a lot of) behind the scenes magic to enable
the recording. There could be bugs that interfere with your code. It is also
possible that you need features that don't yet exist such as the ability to have
multiple use Test::Mimic statements, a package load order and imports in addition
to requires via Test::Mimic. Keep me (Brendan Roof) posted on these issues. I can't
debug your code, but if you can determine why Test::Mimic doesn't work I would be
interested in expanding it's abilities. Patches are great too.

Check to make sure that the .test_mimic_data directory was created in your current
working directory. If it wasn't you probably didn't use Test::Mimic before someone
loaded one of the packages you were trying to mimic. Also, check to make sure that
.test_mimic_data/lib contains the packages you were mimicking in the proper layout.
Don't open them for now. Run your tests again. If they work stop, you're done. If
they don't work, don't be worried -- this is fairly normal. Test::Mimic often needs
specialized options in order to handle certain packages.

Making It Work

First run rm -r .test_mimic_data (or equivalent) to remove your old records.

Changing Some Options

There are a fair number of possible customizations to the standard Test::Mimic
behavior. Some changes may be needed for your code. Here is a template for using
Test::Mimic with every possible customization:

   use Test::Mimic {
       'save'      => '.test_mimic_data',
       'string'    => sub {}, # The sub {} construction simply represents a subroutine reference.
       'destring'  => sub {}, # See below for appropriate contracts.

       'key'           => sub {},
       'monitor_args'  => sub {},
       'play_args'     => sub {},

       'packages'  => {
           'Foo::Bar'  => {
               'scalars'   => [ qw< x y z > ],

               'key'           => sub {},
               'monitor_args'  => sub {},
               'play_args'     => sub {},

               'subs' => {
                   'foo' => {
                       'key'           => sub {},
                       'monitor_args'  => sub {},
                       'play_args'     => sub {},
                   },
               },
           },
       },
   };

Ugh, that's a big hash. Let's tackle the easy parts first. Below, when I say foo,
I really mean the element in the hash with key foo.

    Customizing save simply allows you to choose where recordings are
    written to and read from.

    string must be a a reference to a subroutine that accepts a single
    argument and returns it in a stringified form. It should minimally
    handle non-reference scalars, array references, hash references and
    references to scalars.

    destring must be a reference to a subroutine that is the inverse of
    the 'string' subroutine.

        These subroutines will be used whenever Test::Mimic needs to store
        data in a stable form. The default behavior is to use
        Data::Dump::Streamer if possible and Data::Dumper otherwise.

    scalars is a reference to a list of scalars in the containing package
    that should be mimicked. By default we do not keep track of scalars,
    but we do keep track of arrays and (most) hashes. You only need to
    list a scalar here if code from a non-mimicked package interacts with
    it.

Now for the tough stuff: key, monitor_args and play_args.

    key is a map (in the form of a subroutine reference) from subroutine
    arguments to hash keys. It will be used to determine how a mimicked
    subroutine should behave given certain arguments. Read the POD in
    Test::Mimic and Test::Mimic::Library for more information. Seriously,
    read it. It's more complex than it sounds. If the key generated is not
    specific enough Test::Mimic falls back to relying on call order to
    determine behavior. This could break things if the call order is not
    exactly the same from the recording phase to the playback phase.

Now suppose that we have a subroutine, which, instead of returning a
result, stores a result in a passed reference. How can Test::Mimic
handle this?

    It just so happens that Test::Mimic::Library contains a
    monitor subroutine. Monitor takes a single argument and hijacks (ties)
    it if it is a reference and merely notes its value if it is a simple
    scalar. After tying it modifications to the dereferenced value are
    recorded as long as the value exists. monitor returns a scalar that
    allows Test::Mimic::Library::play to reconstitute the argument to
    monitor. This will also be tied and it will play back its recorded
    history. This process is recursive. There is, however, one problem.
    play generates an entirely new reference. In the playback stage you
    probably want to hijack an incoming argument. The defaults should
    handle this, but at the moment it is difficult for the user to do
    this. Look for a Test::Mimic::Library::hijack subroutine shortly.

    Anyways, monitor_args is reference to a subroutine that accepts a
    reference to an array of arguments, monitors them, and returns a
    scalar that allows play_args to reconstitute all of them. Typically it
    employs Test::Mimic::Library::monitor.

    play_args is a reference to a subroutine that is the inverse of
    monitor_args '. It takes a reference to an array of arguments to
    hijack and the scalar returned by the monitor_args subroutine. It
    should employ the upcoming Test::Mimic::Library::hijack subroutine.
    play_args and monitor_args give you the ability to select which
    arguments are actually important enough to record.

    key, monitor_args and play_args can be given at different portions of
    the options hash. The most specific one possible will be used in each
    case.

After you have made all your changes run your tests again to record
and once more to play.

Other Issues

Test::Mimic can not mimic modifications to global state that the
original package did behind the scenes. You need to be aware as to the
extent that your package does this. If it does all hope is not yet lost.
Test::Mimic generates actual replacement .pm files for each package.
You can edit these to simulate, or actually make, the modifications to
global state. This can be quite complex, but also crucial. You should make
the modifications after the recording phase, but before the playback phase.
One common case of modification of global state is requiring/using other
packages. We will attempt to handle this automatically in a future release,
but for now you may be able to bypass this by adding in the appropriate
require/use statements. You will also probably want to delete the mimicked
subroutines for anything that is imported. You can also edit the code files
if you need to slightly change the behavior of a subroutine. You probably
will not want to edit the behavior hashes that are embedded in the code, but
the subroutines themselves are good candidates.

KNOWN ISSUES

Test::Mimic uses Data::Dump::Streamer if available. If you have it installed the
tests may not pass. The recorded data needed for the tests can currently only be
in one format and I have chosen to have Data::Dumper be the default. This will
likely not cause any problems in actually running Test::Mimic. Either way, look
for this to be fixed in the next release. (If you are really concerned, run the
tests for Test::Mimic::Recorder and use the recorded data in Test::Mimic::Generator's
tests. Next use the generated code in Test::Mimic's tests.)

Test::Mimic takes up a lot of space. Some ways to ameliorate this issue...
The string and destring subroutines are used for almost all stringification purposes.
Switch from the defaults to a more compact representation. Change key, monitor_args 
and play_args wherever possible so that they store less information.

Test::Mimic is slow and tends to pause at the end of program execution.
Remember, this is for testing not production use! As with a debugger extra overhead
is introduced. The pause at the end of program execution is due to Test::Mimic cleaning
up, writing data and generating code.

fork breaks playback. For now, avoid using Test::Mimic on programs where fork is used.
A fix is in the works.

THANKS

Made possible by WhitePages Inc.

Concept by Tye McQueen.
And thanks to many more at WhitePages. You know who you are!

COPYRIGHT AND LICENCE

Copyright (C) 2009 by Brendan Roof

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.8 or,
at your option, any later version of Perl 5 you may have available.


