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

NAME

Test::Sweet - Moose-based Test::Class replacement

VERSION

version 0.03

SYNOPSIS

Write test classes:

   use MooseX::Declare;
   class t::RecordBasic with t::lib::FakeDatabase {
       use Test::Sweet;

       test add_record {
           $self->database->insert( 42 => 'OH HAI' );
           ok $self->database->get_record('42'), 'can get record 42';
       }

       test delete_record {
           ok $self->database->exists('42'), 'still have record 42';
           lives_ok {
               $self->database->delete('42')
           } 'deleting 42 lives';
           ok !$self->database->exists('42'), 'record 42 is gone';
       }
   }

Run them:

   $ mx-run -Ilib t::RecordBasic

And get the valid TAP output:

   1..2
     ok 1 - can get record 42
     1..1
   ok 1 - subtest add_record
     ok 1 - still have record 42
     ok 2 - deleting 42 lives
     ok 3 - record 42 is gone
     1..3
   ok 2 - subtest delete_record

No more counting tests; this module does it for you and ensures that you are protected against premature death. (Well, your test suite, anyway.)

You can also have command-line args for your tests; they are parsed with MooseX::Getopt (if you have it installed; try "mx-run t::YourTest --help").

DESCRIPTION

Test::Sweet lets you organize tests into Moose classes and Moose roles. You just need to create a normal class or role and say use Test::Sweet somewhere. This adds the necessary methods to your metaclass, makes your class do Test::Sweet::Runnable (so that you can run it with MooseX::Runnable's mx-run command), and makes the test keyword available for your use. (The imports are package-scoped, of course, but the test keyword is lexically scoped.)

Normal methods are defined normally. Methods that run tests are defined like methods, but with the test keyword instead of sub or method. In the test methods, you can use any Test::Builder-aware test methods. You get all of Test::More and Test::Exception by default.

Tests can be called as methods any time the test suite is running, including in BUILD and DEMOLISH. Everything will Just Work. The method will get the arguments you pass, you will get the return value, and this module will do what's necessary to ensure that Test::Builder knows what is going on. It's a Moose class and tests are just special methods. Method modifiers work too. (But don't run tests directly in the method modifier body yet; just call other test methods.)

To run all tests in a class (hierarchy), just call the run method.

Tests are ordered as follows. All test method from the superclasses are run first, then your tests are run in the order they appear in the file (this is guaranteed, not a side-effect of anything), then any tests you composed in from roles are run. If anything in the hierarchy overrides a test method from somewhere else in the hierarchy, the overriding method will be run when the original method would be.

Here's an example:

  class A { use Test::Sweet; test first { pass 'first' } };
  class B extends A { use Test::Sweet; test second { pass 'second' } };

When you call A->run, "first" will be run.

When you call B->run, "first" will run, then "second" will run.

If you change B to look like:

  class B extends A {
      test second { pass 'second' }
      test first  { pass 'blah'   }
  }

When you run B->run, first will be called first but will print "blah", and second will be called second. (If you remove the "extends A", they will run in the order they appear in B, of course; second then first.)

METATEST CLASSES AND TRAITS

With this, you can inherit tests from classes or roles, but you can't inherit parts of tests. For example, you may want many tests that will not kill the test suite if they die, but they will fail a subtest in that case. With Test traits, you can implement this behavior reusably; as a CPAN module, as a role in your application, or as a role defined inside the test.

The first thing you need to do is to create a meta-test trait. This is a role that is applied to the metatest class, which is what actually runs each test. This class has "run" method which runs the code you typed into the test file. Metatest traits modify this method (but can have other methods and attributes).

Here is the role that implements the metatest trait that will add a subtest that passes if the body lives, or fails if the body dies:

   role Test::Sweet::Meta::Test::Trait::TestLives {
       use Test::More;

       around run(@args){
           my $lived = 0;
           eval {
               $self->$orig(@args);
               $lived = 1;
           };
           ok $lived, 'test lived ok';
       }
   }

(Note that the Test::Sweet::Meta::Test::Trait:: namespace is the default place for these traits. You can use any namespace you like, however; you just need to prefix the name of the trait with a + when you use something other than this default.)

Now you can use this to modify tests:

    class t::Whatever {
        use Test::Sweet;

        test perhaps_it_lives (TestLives) {
            ok 1, 'got here';
            rand > 0.5 and die 'OH NOES';
        }
    }

You can also use these roles to provide per-test setup and teardown (per-test-class setup and teardown is just BUILD and DEMOLISH). Here's an example test that logs a message when a test starts and when it finishes:

    role Log with MooseX::LogDispatch {
        method BUILD    { $self->logger->info('Test starting') }
        method DEMOLISH { $self->logger->info('Test ending')   }
    }

Now tests that use this metatest trait:

    test foo (+Log) {
        ...
    }

will print a log message when the test starts and when it ends. (You can actually do this with around run too.)

The test metainstance is always available inside your test as $test:

    test foo (+Log) {
        $test->logger->info('Inside the test.');
    }

(You also get $self inside tests, if you didn't notice that already. $self is the test class instance, $test is the metatest object, and $self->meta is the test metaclass. So much meta...)

Finally, the examples above only used one trait per test, but you can apply as many as you want:

    test foo (+Foo, Bar, Baz, +Quux, OH::Hai) {
        ...
    }

REPOSITORY

http://github.com/jrockway/test-sweet

Patches (or pull requests) are very welcome. You should also discuss this module on the moose irc channel at irc://irc.perl.org/#moose; nothing is set in stone yet, and your feedback is requested.

TODO

Convince prove to run the <.pm> files directly.

Write code to organize classes into test suites; and run the test suites easily. (Classes and tests should be tagged, so you can run run-test-suite t::Suite --no-slow-tests or something.)

More testing. There are undoubtedly corner cases that are undiscovered and unhandled.

Document the full meta-protocol. For now, look at the t::Traits test.

Syntax sugar for creating traits and attributes on those traits.

SEE ALSO

http://github.com/jrockway/test-sweet-dbic shows what sort of reusability you can get with Test::Sweet... with 5 minutes of hacking.

Read this module's test suite (in the t/ directory) for example of how to make prove understand Test::Sweet classes.

AUTHOR

Jonathan Rockway <jrockway@cpan.org>

COPYRIGHT

Copyright (c) 2009 Jonathan Rockway.

This module is free software, you may redistribute it under the same terms as Perl itself.

AUTHOR

Jonathan Rockway <jrockway@cpan.org>

COPYRIGHT AND LICENSE

This software is copyright (c) 2011 by Jonathan Rockway.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.