The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Test::SynchHaveWant - Synchronize volatile have/want values for tests

VERSION

Version 0.01

SYNOPSIS

    use Test::Most;
    use Test::SynchHaveWant qw/
        have
        want
    /;

    my $have = some_complex_data();

    eq_or_diff have($have),  want(), 'have and want should be the same';

    __DATA__
    [
        {
            'bar' => [ 3, 4 ],
            'foo' => 1
        },
        0,
        bless( [ 'this', 'that', 'glarble', 'fetch' ], 'Foobar' ),
    ]

If you wish to synch:

    use Test::Most;
    use Test::SynchHaveWant qw/
        have
        want
        synch
    /;

    my $have = some_complex_data();

    eq_or_diff have($have),  want(), 'have and want should be the same';
    is have(0), want(), '0 is 0';

    # note that we can use normal tests
    my $want = want();
    isa_ok $want, 'Foobar';
    is_deeply $have($some_object), $want, '... and the object is the same';
    synch();

    __DATA__
    [
        {
            'bar' => [ 3, 4 ],
            'foo' => 1
        },
        0,
        bless( [ 'this', 'that', 'glarble', 'fetch' ], 'Foobar' ),
    ]

DO NOT USE THIS CODE WITHOUT SOURCE CONTROL

This is ALPHA CODE. It's very alpha code. It's dangerous code. It attempts to REWRITE YOUR TESTS and if it screws up, you had better be using SOURCE CONTROL so you can revert.

That being said, if you need this code and you really, really understand what's going on, go ahead and use it at your own risk.

DESCRIPTION

Sometimes you have extremely volatile data/code and you know your tests are correct even though they've failed because the code has changed or the underlying data has been altered. Ordinarily, you never, never want your tests to be so fragile. You want to figure out some way of mocking your test data or isolating functional units in your code for testing.

The first pass I had at solving this problem was to effectively compute the edit distance for data structures, but even that failed as differences emerged over time (see http://blogs.perl.org/users/ovid/2011/02/is-almost.html).

For this module, we're giving devs a chance to rewrite their test results on the fly, assuming that the new results of their code is correct.

This is generally an INCREDIBLY STUPID IDEA. It's very stupid. Not only do we attempt to rewrite your __DATA__ sections, we make it very easy for you to have bogus tests because you may incorrectly assume that the new data you're returning is correct. That's why this is a BIG, FAT, DANGEROUS EXPERIMENT.

I've been asked a couple of times why I feel the need to experiment with writing "fragile" tests but I can't tell you due to my NDA.

WHY IS OVID BEING STUPID?

Tests should not be as fragile as indicated here. You should mock up your test data or find ways of isolating functionality to make your tests more robust.

Not everyone has that luxury. If you insist that everyone does have that luxury, be aware that the real world of "these are the constraints I have" and the fantasy world of "the way I like things is the only way things should be done" aren't on speaking terms to one another.

USAGE

To make this work, you must have a __DATA__ section in your code. This section should contain terse Data::Dumper output of an array reference with each value being a subsequent expected test result. Every time want() is called, the next value in this array ref is returned:

    is have($foo),         want();    # 3
    is_deeply have($aref), want();    # ['foo','bar']
    is have($idiot),       want();    # 'ovid'
    __DATA__
    [
       3,
       [ qw/foo bar/ ],
       'ovid',
    ]

The have() function must be called as often as the want() function (and in sequence) to track the values we have received.

If desired, the synch() function may be exported and called at the end of the test run. If any tests failed (! Test::Builder->new->is_passing), then we attempt to write all values passed to have() to the __DATA__ section.

synch() will fail if have/want have been called a different number of times or if it has already been called. have() and want() will fail if synch() has already been called.

It goes without saying that this means you must have a deterministic order for your tests. Bad:

    while ( my ( $key, $value ) = each %test ) {
        is_deeply have( some_func( $key, $value ) ), want();
    }

Good:

    foreach my $key  ( sort keys %test ) {
        my $value = $test{$key};
        is_deeply have( some_func( $key, $value ) ), want();
    }

EXPORT

have

 is have($have), want(), 'have should equal want';

Ordinarily this function is a no-op. It merely returns the value it is passed. However, if synch is called at the end of the test run, the values passed to this function will be written to the data in the __DATA__ section.

want

 is have($have), want(), 'have should equal want';

Returns the current expected test result. Attempting to read past the end of the test results will result in a fatal error.

synch

    synch();

This function will attempt to take all of the values passed to have() and write them out to the __DATA__ section. If have() and want() have been called an unequal number of times, this function will die.

Will not attempt to synch the __DATA__ if the tests appear to be passing.

If tests are not passing, will prompt the user if they really want to synch tests results. Only a /^\s*[Yy]/ is acceptable. To ensure that we don't block on automated systems, we have an alarm set for 10 seconds. After that, we merely return without attempting to synch.

AUTHOR

Curtis 'Ovid' Poe, <ovid at cpan.org>

BUGS

Please report any bugs or feature requests to bug-test-synchhavewant at rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Test-SynchHaveWant. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

SUPPORT

You can find documentation for this module with the perldoc command.

    perldoc Test::SynchHaveWant

You can also look for information at:

ACKNOWLEDGEMENTS

You don't really think I'm going to blame anyone else for this idiocy, do you?

LICENSE AND COPYRIGHT

Copyright 2011 Curtis 'Ovid' Poe.

This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License.

See http://dev.perl.org/licenses/ for more information.