NAME¶
Test::Roo - Composable, reusable tests with roles and Moo
VERSION¶
version 1.004
SYNOPSIS¶
Define test behaviors and required fixtures in a role:
# t/lib/ObjectCreation.pm
package ObjectCreation;
use Test::Roo::Role; # loads Moo::Role and Test::More
requires 'class'; # we need this fixture
test 'object creation' => sub {
my $self = shift;
require_ok( $self->class );
my $obj = new_ok( $self->class );
};
1;
Provide fixtures and run tests from the .t file:
# t/test.t
use Test::Roo; # loads Moo and Test::More
use lib 't/lib';
# provide the fixture
has class => (
is => 'ro',
default => sub { "Digest::MD5" },
);
# specify behaviors to test
with 'ObjectCreation';
# give our subtests a pretty label
sub _build_description { "Testing " . shift->class }
# run the test with default fixture
run_me;
# run the test with different fixture
run_me( { class => "Digest::SHA1" } );
done_testing;
Result:
$ prove -lv t
t/test.t ..
ok 1 - require Digest::MD5;
ok 2 - The object isa Digest::MD5
1..2
ok 1 - object creation
1..1
ok 1 - Testing Digest::MD5
ok 1 - require Digest::SHA1;
ok 2 - The object isa Digest::SHA1
1..2
ok 1 - object creation
1..1
ok 2 - Testing Digest::SHA1
1..2
ok
All tests successful.
Files=1, Tests=2, 0 wallclock secs ( 0.02 usr 0.01 sys + 0.06 cusr 0.00 csys = 0.09 CPU)
Result: PASS
DESCRIPTION¶
This module allows you to compose Test::More tests from roles. It is inspired by
the excellent Test::Routine module, but uses Moo instead of Moose. This gives
most of the benefits without the need for Moose as a test dependency.
Test files are Moo classes. You can define any needed test fixtures as Moo
attributes. You define tests as method modifiers -- similar in concept to
"subtest" in Test::More, but your test method will be passed the
test object for access to fixture attributes. You may compose any Moo::Role
into your test to define attributes, require particular methods, or define
tests.
This means that you can isolate test
behaviors into roles which require
certain test
fixtures in order to run. Your main test file will provide
the fixtures and compose the roles to run. This makes it easy to reuse test
behaviors.
For example, if you are creating tests for Awesome::Module, you could create the
test behaviors as Awesome::Module::Test::Role and distribute it with your
module. If another distribution subclasses Awesome::Module, it can compose the
Awesome::Module::Test::Role behavior for its own tests.
No more copying and pasting tests from a super class! Superclasses define and
share their tests. Subclasses provide their own fixtures and run the tests.
USAGE¶
Importing Test::Roo also loads Moo (which gives you strictures with fatal
warnings and other goodies) and makes the current package a subclass of
Test::Roo::Class.
Importing also loads Test::More. No test plan is used. The
"done_testing" function must be used at the end of every test file.
Any import arguments are passed through to Test::More's "import"
method.
See also Test::Roo::Role for test role usage.
Creating fixtures¶
You can create fixtures with normal Moo syntax. You can even make them lazy if
you want:
has fixture => (
is => 'lazy'
);
sub _build_fixture { ... }
This becomes really useful with Test::Roo::Role. A role could define the
attribute and require the builder method to be provided by the main test
class.
Composing test roles¶
You can use roles to define units of test behavior and then compose them into
your test class using the "with" function. Test roles may define
attributes, declare tests, require certain methods and anything else you can
regularly do with roles.
use Test::Roo;
with 'MyTestRole1', 'MyTestRole2';
See Test::Roo::Role and the Test::Roo::Cookbook for details and examples.
Setup and teardown¶
You can add method modifiers around the "setup" and
"teardown" methods and these will be run before tests begin and
after tests finish (respectively).
before setup => sub { ... };
after teardown => sub { ... };
You can also add method modifiers around "each_test", which will be
run before and after
every individual test. You could use these to
prepare or reset a fixture.
has fixture => ( is => 'lazy, clearer => 1, predicate => 1 );
after each_test => sub { shift->clear_fixture };
Roles may also modify "setup", "teardown", and
"each_test", so the order that modifiers will be called will depend
on when roles are composed. Be careful with "each_test", though,
because the global effect may make composition more fragile.
You can call test functions in modifiers. For example, you could confirm that
something has been set up or cleaned up.
before each_test => sub { ok( ! shift->has_fixture ) };
Running tests¶
The simplest way to use Test::Roo with a single
.t file is to let the
"main" package be the test class and call "run_me" in it:
# t/test.t
use Test::Roo; # loads Moo and Test::More
has class => (
is => 'ro',
default => sub { "Digest::MD5" },
);
test 'load class' => sub {
my $self = shift;
require_ok( $self->class );
}
run_me;
done_testing;
Calling "run_me(@args)" is equivalent to calling
"__PACKAGE__->run_tests(@args)" and runs tests for the current
package.
You may specify an optional description or hash reference of constructor
arguments to customize the test object:
run_me( "load MD5" );
run_me( { class => "Digest::MD5" } );
run_me( "load MD5", { class => "Digest::MD5" } );
See Test::Roo::Class for more about the "run_tests" method.
Alternatively, you can create a separate package (in the test file or in a
separate
.pm file) and run tests explicitly on that class.
# t/test.t
package MyTest;
use Test::Roo;
use lib 't/lib';
has class => (
is => 'ro',
required => 1,
);
with 'MyTestRole';
package main;
use strictures;
use Test::More;
for my $c ( qw/Digest::MD5 Digest::SHA/ ) {
MyTest->run_tests("Testing $c", { class => $c } );
}
done_testing;
EXPORTED FUNCTIONS¶
Loading Test::Roo exports subroutines into the calling package to declare and
run tests.
test¶
test $label => sub { ... };
The "test" function adds a subtest. The code reference will be called
with the test object as its only argument.
Tests are run in the order declared, so the order of tests from roles will
depend on when they are composed relative to other test declarations.
top_test¶
top_test $label => sub { ... };
The "top_test" function adds a "top level" test. Works
exactly like "test" except it will not start a subtest. This is
especially useful in very simple testing situations where the extra subtest
level is just noise.
So for example the following test
# t/test.t
use Test::Roo;
has class => (
is => 'ro',
required => 1,
);
top_test basic => sub {
my $self = shift;
require_ok($self->class);
isa_ok($self->class->new, $self->class);
};
for my $c ( qw/Digest::MD5 Digest::SHA/ ) {
run_me("Testing $c", { class => $c } );
}
done_testing;
produces the following TAP
t/test.t ..
ok 1 - require Digest::MD5;
ok 2 - The object isa Digest::MD5
1..2
ok 1 - Testing Digest::MD5
ok 1 - require Digest::SHA1;
ok 2 - The object isa Digest::SHA1
1..2
ok 2 - Testing Digest::SHA1
1..2
ok
All tests successful.
Files=1, Tests=2, 0 wallclock secs ( 0.02 usr 0.01 sys + 0.06 cusr 0.00 csys = 0.09 CPU)
Result: PASS
run_me¶
run_me;
run_me( $description );
run_me( $init_args );
run_me( $description, $init_args );
The "run_me" function calls the "run_tests" method on the
current package and passes all arguments to that method. It takes a
description and/or a hash reference of constructor arguments.
DIFFERENCES FROM TEST::ROUTINE¶
While this module was inspired by Test::Routine, it is not a drop-in
replacement. Here is an overview of major differences:
- •
- Test::Roo uses Moo; Test::Routine uses Moose
- •
- Loading Test::Roo makes the importing package a class; in Test::Routine it
becomes a role
- •
- Loading Test::Roo loads Test::More; Test::Routine does not
- •
- In Test::Roo, "run_test" is a method; in Test::Routine it is a
function and takes arguments in a different order
- •
- In Test::Roo, all role composition must be explicit using
"with"; in Test::Routine, the "run_tests" command can
also compose roles
- •
- In Test::Roo, test blocks become method modifiers hooked on an empty
method; in Test::Routine, they become methods run via introspection
- •
- In Test::Roo, setup and teardown are done by modifying "setup"
and "teardown" methods; in Test::Routine they are done by
modifying "run_test"
SUPPORT¶
Bugs / Feature Requests¶
Please report any bugs or feature requests through the issue tracker at
<
https://github.com/dagolden/Test-Roo/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/dagolden/Test-Roo>
git clone https://github.com/dagolden/Test-Roo.git
AUTHOR¶
David Golden <dagolden@cpan.org>
CONTRIBUTORS¶
- •
- Arthur Axel 'fREW' Schmidt <frioux@gmail.com>
- •
- Diab Jerius <djerius@gmail.com>
COPYRIGHT AND LICENSE¶
This software is Copyright (c) 2013 by David Golden.
This is free software, licensed under:
The Apache License, Version 2.0, January 2004