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

NAME

Test::Class::Sugar - Helper syntax for writing Test::Class tests

SYNOPSIS

    use Test::Class::Sugar;

    testclass exercises Person {
        # Test::Most has been magically included
        # 'warnings' and 'strict' are turned on

        startup >> 1 {
            use_ok $test->subject;
        }

        test autonaming {
            is ref($test), 'Test::Person';
        }

        test the naming of parts {
            is $test->current_method, 'test_the_naming_of_parts';
        }

        test multiple assertions >> 2 {
            is ref($test), 'Test::Person';
            is $test->current_method, 'test_multiple_assertions';
        }
    }

    Test::Class->runtests;

DESCRIPTION

Test::Class::Sugar provides a new syntax for setting up your Test::Class based tests. The idea is that we bundle up all the tedious boilerplate involved in writing a class in favour of getting to the meat of what you're testing. We made warranted assumptions about what you want to do, and we do them for you. So, when you write

    testclass exercises Person {
        ...
    }

What Perl sees, after Test::Class::Sugar has done its work, is roughly:

    {
        package Test::Person;
        use base qw/Test::Class/;
        use strict; use warnings;
        require Person;

        sub subject { 'Person' };

        ...
    }

Some of the assumptions we made are overrideable, others aren't. Yet. Most of them will be though. See "Changing Assumptions" for details

Why you shouldn't use Test::Class::Sugar

Test::Class::Sugar is very new, mostly untested and is inadvertently hostile to you if you confuse its parser. Don't use it if you want to live.

Why you should use Test::Class::Sugar

It's so shiny! Test::Class::Sugar was written to scratch an itch I had when writing some tests for a MooseX::Declare based module. Switching from the implementation code to the test code was like shifting from fifth to first gear in one fell swoop. Not fun. This is my attempt to sprinkle some Devel::Declare magic dust over the testing experience.

Bear this in mind:

Test::Class::Sugar is not a source filter

I know it looks like a source filter in the right light, but it isn't. Source filters fall down because only perl can parse Perl, so it's easy to confuse them. Devel::Declare based modules work by letting perl parse Perl until it comes across a new keyword, at which point it temporarily hands parsing duty over to a new parser which has the job of parsing the little language introduced by the keyword, turning it into real Perl, and handing the responsibility for parsing that back to Perl. Obviously, it's still possible for that to screw things up royally, but there are fewer opportunities to fuck up.

We now return you to your regularly scheduled documentation.

SYNTAX

Essentially, Test::Class::Sugar adds some new keywords to perl. Here's what they do, and what they expect.

(Syntax is described in the semi-standard half-arsed Backus-Naur Form beloved of crappy language documentation efforts everywhere. If you can't read it by now, find someone who can and blackmail them into writing a BNF free tutorial and I for one will thank you for it.)

testclass
    testclass NAME?
      ( exercises CLASS
      | extends CLASS (, CLASS)*
      | uses HELPER (, HELPER)*
      )*

Where NAME is is an optional test class name - the sort of thing you're used to writing after package. You don't have to name your testclass, but if you don't supply a name, you MUST supply an exercises clause.

exercises CLASS

You can supply at most one exercises clause. This specifies the class under test. We use it to autoname the class if you haven't provided a NAME of your own (the default name of the class would be Test::<CLASS>). Also, if you supply an exercises clause, the class will be autorequired and your test class will have a subject helper method, which will return the name of the class under test.

extends CLASS (, CLASS)*

Sometimes, you don't want to inherit directly from Test::Class. If that's the case, add an extends clause, and your worries will be over. The extends clause supports, but emphatically does not encourage, multiple inheritance. Friends don't let friends do multiple inheritance, but Test::Class::Sugar's not a friend, it's a robot servant which knows nothing of Asimov's Laws. If you insist on asking it for a length of rope with a loop at the end and a rickety stepladder on which to stand, it will be all too happy to assist.

uses HELPER (, HELPER)*

Ah, the glory that is the uses clause. If you don't provide a uses clause, Test::Class::Sugar will assume that you want to use Test::Most as your testing only testing helper library. If you would rather use, say, Test::More then you can do:

    testclass ExampleTest uses -More {...}

Hang on, -More, what's that about? It's a simple shortcut. Instead of making you write uses Test::This, Test::That, Test::TheOther, you can write uses -This, -That, -TheOther and we'll expand the - into Test:: and do the right thing.

Note that, if you need to do anything special in the way of import arguments, you should do the use yourself. We're all about the 80:20 rule here.

test
    test WORD ( WORD )* (>> PLAN)? { ... }

I may be fooling myself, but I hope its mostly obvious what this does. Here's a few examples to show you what's happening:

    test with multiple subtests >> 3 {...}
    test with no_plan >> no_plan {...}
    test 'a complicated description with "symbols" in it' {...}

Gets translated to:

    sub test_with_multiple_subtests : Test(3) {...}
    sub test_with_no_plan : Test(no_plan) {...}
    sub a_complicated_description_with_symbols_in_it : Test {...}

>> PLAN is used to declare the number of subtests run by a given message. It's not the most obvious choice I know, but I gave up on trying to use : after losing a few rounds with Perl over loop labels.

See Test::Class for details of PLAN's semantics.

Lifecycle Methods

startup
setup
teardown
shutdown
    (startup|setup|teardown|shutdown) ( WORD )* (>> PLAN)? { ... }

These lifecycle helpers work in pretty much the same way as "test", but with the added wrinkle that, if you don't supply a name, they generate method names derived from the name of the test class and the name of the helper, so, for instance:

    testclass Test::Lifecycle::Autonaming {
        setup { ... }
    }

is equivalent to writing:

    testclass Test::Lifecycle::Autonaming {
        setup 'setup_Test_Lifecycle_Autonaming' {...}
    }

Other than that, the lifecycle helpers behave as described in Test::Class. In particular, you can still give them names, so

    testclass {
        setup with a name {...}
    }

works just fine.

Changing Assumptions

There are several aspects of Test::Class::Sugar's policy that you may disagree with. If you do, you can adjust them by passing a 'defaults' hash at use time. For example:

    use Test::Class::Sugar defaults => { prefix => TestSuite };

Here's a list of the possible default settings and what they affect.

prefix

Changes the prefix used for autogenerating test class names from Test:: to whatever you specify, so:

    use Test::Class::Sugar defaults => { prefix => TestSuite };

    testclass exercises Something {
        ...
    }

will build a test class called TestSuite::Something

test_instance

COMING SOON

Prefer $self to $test in your test methods? Then the test_instance default is your friend. Just do

    use Test::Class::Sugar defaults => { test_instance => '$self' }

and all manner of things shall be well.

uses

COMING SOON, BUT PROBABLY LATER THAN test_instance

Bored of adding the same old uses clause to your every testclass? Fix it at use time like so:

    use Test::Class::Sugar
        defaults => {
            uses => [qw/Test::More Moose/]
        };

DIAGNOSTICS

Right now, Test::Class::Sugar's diagnostics range from the confusing to the downright misleading. Expect progress on this in the future, tuit supply permitting.

Patches welcome.

BUGS AND LIMITATIONS

Known bugs

Screwy line numbers

Test::Class::Sugar can screw up the accord between the line perl thinks some code is on and the line the code is actually on. This makes debugging test classes harder than it should be. Our error reporting is bad enough already without making things worse.

Unknown bugs

There's bound to be some.

We still don't play well with MooseX::Declare

It would be useful to pinch some of MooseX::Declare's magic for writing helper methods. Something like:

    helper whatever ($arg) {
        lives_ok { $test->subject->new($arg) }
    }

could be rather handy.

Patches welcome.

Please report any bugs or feature requests to me. It's unlikely you'll get any response if you use http://rt.cpan.org though. Your best course of action is to fork the project http://www.github.com/pdcawley/test-class-sugar, write at least one failing test (Write something in testclass form that should work, but doesn't. If you can arrange for it to fail gracefully, then please do, but if all you do is write something that blows up spectacularly, that's good too. Failing/exploding tests are like manna to a maintenance programmer.

AUTHOR

Piers Cawley <pdcawley@bofh.org.uk>

ACKNOWLEDGEMENTS

Thanks to Adrian Howard for the original Test::Class, and to Adam Kennedy for taking on the maintenance of it.

Thanks to my contributors:

Hans Dieter Pearcey for documentation fixes and Joel Bernstein for doing the boring work of making this all work with Perl 5.8 (which means I can start using this at work!)

LICENCE AND COPYRIGHT

Copyright (c) 2009, Piers Cawley <pdcawley@bofh.org.uk>. All rights reserved.

This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See perlartistic.

DISCLAIMER OF WARRANTY

BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR CORRECTION.

IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

2 POD Errors

The following errors were encountered while parsing the POD:

Around line 294:

You forgot a '=back' before '=head2'

Around line 296:

'=item' outside of any '=over'