use strict;
use warnings;
package Test::Routine::Manual::Demo;
# ABSTRACT: a walkthrough, in code, of Test::Routine
$Test::Routine::Manual::Demo::VERSION = '0.020';
#pod =head1 The Demo
#pod
#pod =head2 t/demo/01-demo.t
#pod
#pod #!/bin/env perl
#pod use strict;
#pod use warnings;
#pod
#pod # This test is both a test and an example of how Test::Routine works! Welcome
#pod # to t/01-demo.t, I will be your guide, rjbs.
#pod
#pod {
#pod # This block defines the HashTester package. It's a Test::Routine, meaning
#pod # it's a role. We define state that the test will need to keep and any
#pod # requirements we might have.
#pod #
#pod # Before we can run this test, we'll need to compose the role into a class so
#pod # that we can make an instance.
#pod package HashTester;
#pod use Test::Routine;
#pod
#pod # We import stuff from Test::More because, well, who wants to re-write all
#pod # those really useful test routines that exist out there? Maybe somebody,
#pod # but not me.
#pod use Test::More;
#pod
#pod # ...but then we use namespace::autoclean to get rid of the routines once
#pod # we've bound to them. This is just standard Moose practice, anyway, right?
#pod use namespace::autoclean;
#pod
#pod # Finally, some state! Every test will get called as method on an instance,
#pod # and it will have this attribute. Here are some points of interest:
#pod #
#pod # - We're giving this attribute a builder, so it will try to get built with a
#pod # call to $self->build_hash_to_test -- so each class that composes this
#pod # role can provide means for these attributes (fixtures) to be generated as
#pod # needed.
#pod #
#pod # - We are not adding "requires 'build_hash_to_test'", because then we can
#pod # apply this role to Moose::Object and instantiate it with a given value
#pod # in the constructor. There will be an example of this below. This lets
#pod # us re-use these tests in many variations without having to write class
#pod # after class.
#pod #
#pod # - We don't use lazy_build because it would create a clearer. If someone
#pod # then cleared our lazy_build fixture, it could not be re-built in the
#pod # event that we'd gotten it explicitly from the constructor!
#pod #
#pod # Using Moose attributes for our state and fixtures allows us to get all of
#pod # their powerful behaviors like types, delegation, traits, and so on, and
#pod # allows us to decompose shared behavior into roles.
#pod #
#pod has hash_to_test => (
#pod is => 'ro',
#pod isa => 'HashRef',
#pod builder => 'build_hash_to_test',
#pod );
#pod
#pod # Here, we're just declaring an actual test that we will run. This sub will
#pod # get installed as a method with a name that won't get clobbered easily. The
#pod # method will be found later by run_tests so we can find and execute all
#pod # tests on an instance.
#pod #
#pod # There is nothing magical about this method! Calling this method is
#pod # performed in a Test::More subtest block. A TAP plan can be issued with
#pod # "plan", and we can issue TODO or SKIP directives the same way. There is
#pod # none of the return-to-skip magic that we find in Test::Class.
#pod #
#pod # The string after "test" is used as the method name -- which means we're
#pod # getting a method name with spaces in it. This can be slightly problematic
#pod # if you try to use, say, ::, in a method name. For the most part, it works
#pod # quite well -- but look at the next test for an example of how to give an
#pod # explicit description.
#pod test "only one key in hash" => sub {
#pod my ($self) = @_;
#pod
#pod my $hash = $self->hash_to_test;
#pod
#pod is(keys %$hash, 1, "we have one key in our test hash");
#pod is(2+2, 4, "universe still okay");
#pod };
#pod
#pod # The only thing of note here is that we're passing a hashref of extra args
#pod # to the test method constructor. "desc" lets us set the test's description,
#pod # which is used in the test output, so we can avoid weird method names being
#pod # installed. Also note that we order tests more or less by order of
#pod # definition, not by name or description.
#pod test second_test => { desc => "Test::Routine demo!" } => sub {
#pod pass("We're running this test second");
#pod pass("...notice that the subtest's label is the 'desc' above");
#pod pass("...and not the method name!");
#pod };
#pod }
#pod
#pod {
#pod # This package is one fixture against which we can run the HashTester
#pod # routine. It has the only thing it needs: a build_hash_to_test method.
#pod # Obviously real examples would have more to them than this.
#pod package ProcessHash;
#pod use Moose;
#pod with 'HashTester';
#pod
#pod use namespace::autoclean;
#pod
#pod sub build_hash_to_test { return { $$ => $^T } }
#pod }
#pod
#pod # Now we're into the body of the test program: where tests actually get run.
#pod
#pod # We use Test::Routine::Util to get its "run_tests" routine, which runs the
#pod # tests on an instance, building it if needed.
#pod use Test::Routine::Util;
#pod
#pod # We use Test::More to get done_testing. We don't assume that run_tests is the
#pod # entire test, because that way we can (as we do here) run multiple test
#pod # instances, and can intersperse other kinds of sanity checks amongst the
#pod # Test::Routine-style tests.
#pod use Test::More;
#pod
#pod is(2+2, 4, "universe still makes sense") or BAIL_OUT("PANIC!");
#pod
#pod # The first arg is a description for the subtest that will be run. The second,
#pod # here, is a class that will be instantiated and tested.
#pod run_tests('ProcessHash class' => 'ProcessHash');
#pod
#pod # Here, the second argument is an instance of a class to test.
#pod run_tests('ProcessHash obj' => ProcessHash->new({ hash_to_test => { 1 => 0 }}));
#pod
#pod # We could also just supply a class name and a set of args to pass to new.
#pod # The below is very nearly equivalent to the above:
#pod run_tests('ProcessHash new' => ProcessHash => { hash_to_test => { 1 => 0 }});
#pod
#pod # ...and here, the second arg is not a class or instance at all, but the
#pod # Test::Routine role (by name). Since we know we can't instantiate a role,
#pod # run_tests will try to compose it with Moose::Object. Then the args are used
#pod # as the args to ->new on the new class, as above. This lets us write
#pod # Test::Routines that can be tested with the right state to start with, or
#pod # Test::Routines that need to be composed with testing fixture classes.
#pod run_tests(
#pod 'HashTester with given state',
#pod HashTester => {
#pod hash_to_test => { a => 1 },
#pod },
#pod );
#pod
#pod # There's one more interesting way to run out tests, but it's demonstrated in
#pod # 02-simple.t instead of here. Go check that out.
#pod
#pod # ...and we're done!
#pod done_testing;
#pod
#pod
#pod =head2 t/demo/02-simple.t
#pod
#pod # Welcome to part two of the Test::Routine demo. This is showing how you can
#pod # write quick one-off tests without having to write a bunch of .pm files or
#pod # (worse?) embed packages in bare blocks in the odious way that 01-demo.t did.
#pod #
#pod # First off, we use Test::Routine. As it did before, this turns the current
#pod # package (main!) into a Test::Routine role. It also has the pleasant
#pod # side-effect of turning on strict and warnings.
#pod use Test::Routine;
#pod
#pod # Then we bring in the utils, because we'll want to run_tests later.
#pod use Test::Routine::Util;
#pod
#pod # And, finally, we bring in Test::More so that we can use test assertions, and
#pod # namespace::autoclean to clean up after us.
#pod use Test::More;
#pod use namespace::autoclean;
#pod
#pod # We're going to give our tests some state. It's nothing special.
#pod has counter => (
#pod is => 'rw',
#pod isa => 'Int',
#pod default => 0,
#pod );
#pod
#pod # Then another boring but useful hunk of code: a method for our test routine.
#pod sub counter_is_even {
#pod my ($self) = @_;
#pod return $self->counter % 2 == 0;
#pod }
#pod
#pod # Then we can write some tests, just like we did before. Here, we're writing
#pod # several tests, and they will be run in the order in which they were defined.
#pod # You can see that they rely on the state being maintained.
#pod test 'start even' => sub {
#pod my ($self) = @_;
#pod ok($self->counter_is_even, "we start with an even counter");
#pod
#pod $self->counter( $self->counter + 1);
#pod };
#pod
#pod test 'terminate odd' => sub {
#pod my ($self) = @_;
#pod
#pod ok(! $self->counter_is_even, "the counter is odd, so state was preserved");
#pod pass("for your information, the counter is " . $self->counter);
#pod };
#pod
#pod # Now we can run these tests just by saying "run_me" -- rather than expecting a
#pod # class or role name, it uses the caller. In this case, the calling package
#pod # (main!) is a Test::Routine, so the runner composes it with Moose::Object,
#pod # instantiating it, and running the tests on the instance.
#pod run_me;
#pod
#pod # Since each test run gets its own instance, we can run the test suite again,
#pod # possibly to verify that the test suite is not destructive of some external
#pod # state.
#pod run_me("second run");
#pod
#pod # And we can pass in args to use when constructing the object to be tested.
#pod # Given the tests above, we can pick any starting value for "counter" that is
#pod # even.
#pod run_me({ counter => 192 });
#pod
#pod # ...and we're done!
#pod done_testing;
#pod
#pod # More Test::Routine behavior is demonstrated in t/03-advice.t and t/04-misc.t
#pod # Go have a look at those!
#pod
#pod
#pod =head2 t/demo/03-advice.t
#pod
#pod use Test::Routine;
#pod use Test::Routine::Util;
#pod use Test::More;
#pod
#pod use namespace::autoclean;
#pod
#pod # xUnit style testing has the idea of setup and teardown that happens around
#pod # each test. With Test::Routine, we assume that you will do most of this sort
#pod # of thing in your BUILD, DEMOLISH, and attribute management. Still, you can
#pod # easily do setup and teardown by applying method modifiers to the "run_test"
#pod # method, which your Test::Routine uses to run each test. Here's a simple
#pod # example.
#pod
#pod # We have the same boring state that we saw before. It's just an integer that
#pod # is carried over between tests.
#pod has counter => (
#pod is => 'rw',
#pod isa => 'Int',
#pod lazy => 1,
#pod default => 0,
#pod clearer => 'clear_counter',
#pod );
#pod
#pod # The first test changes the counter's value and leaves it changed.
#pod test test_0 => sub {
#pod my ($self) = @_;
#pod
#pod is($self->counter, 0, 'start with counter = 0');
#pod $self->counter( $self->counter + 1);
#pod is($self->counter, 1, 'end with counter = 1');
#pod };
#pod
#pod # The second test assumes that the value is the default, again. We want to
#pod # make sure that before each test, the counter is reset, but we don't want to
#pod # tear down and recreate the whole object, because it may have other, more
#pod # expensive resources built.
#pod test test_1 => sub {
#pod my ($self) = @_;
#pod
#pod is($self->counter, 0, 'counter is reset between tests');
#pod };
#pod
#pod # ...so we apply a "before" modifier to each test run, calling the clearer on
#pod # the counter. When next accessed, it will re-initialize to zero. We could
#pod # call any other code we want here, and we can compose numerous modifiers
#pod # together onto run_test.
#pod #
#pod # If you want to clear *all* the object state between each test... you probably
#pod # want to refactor.
#pod before run_test => sub { $_[0]->clear_counter };
#pod
#pod run_me;
#pod done_testing;
#pod
#pod
#pod =head2 t/demo/04-misc.t
#pod
#pod use Test::Routine;
#pod use Test::Routine::Util;
#pod use Test::More;
#pod
#pod use namespace::autoclean;
#pod
#pod # One thing that the previous examples didn't show was how to mark tests as
#pod # "skipped" or "todo." Test::Routine makes -no- provisions for these
#pod # directives. Instead, it assumes you will use the entirely usable mechanisms
#pod # provided by Test::More.
#pod
#pod # This is a normal test. It is neither skipped nor todo.
#pod test boring_ordinary_tests => sub {
#pod pass("This is a plain old boring test that always passes.");
#pod pass("It's here just to remind you what they look like.");
#pod };
#pod
#pod # To skip a test, we just add a "skip_all" plan. Because test methods get run
#pod # in subtests, this skips the whole subtest, but nothing else.
#pod test sample_skip_test => sub {
#pod plan skip_all => "these tests don't pass, for some reason";
#pod
#pod is(6, 9, "I don't mind.");
#pod };
#pod
#pod # To mark a test todo, we just set our local $TODO variable. Because the test
#pod # is its own block, this works just like it would in any other Test::More test.
#pod test sample_todo_test => sub {
#pod local $TODO = 'demo of todo';
#pod
#pod is(2 + 2, 5, "we can bend the fabric of reality");
#pod };
#pod
#pod run_me;
#pod done_testing;
#pod
#pod
#pod =head2 t/demo/05-multiple.t
#pod
#pod #!/bin/env perl
#pod use strict;
#pod use warnings;
#pod
#pod use Test::Routine::Util;
#pod use Test::More;
#pod
#pod # One of the benefits of building our sets of tests into roles instead of
#pod # classes is that we can re-use them in whatever combination we want. We can
#pod # break down sets of tests into bits that can be re-used in different cases.
#pod # With classes, this would lead to multiple inheritance or other monstrosities.
#pod
#pod # Here's a first Test::Routine. We use it to make sure that one of our
#pod # fixture's attributes is a numeric id.
#pod {
#pod package Test::ThingHasID;
#pod use Test::Routine;
#pod use Test::More;
#pod
#pod requires 'id';
#pod
#pod test thing_has_numeric_id => sub {
#pod my ($self) = @_;
#pod
#pod my $id = $self->id;
#pod like($id, qr/\A[0-9]+\z/, "the thing's id is a string of ascii digits");
#pod };
#pod }
#pod
#pod # A second one ensures that the thing has an associated directory that
#pod # looks like a unix path.
#pod {
#pod package Test::HasDirectory;
#pod use Test::Routine;
#pod use Test::More;
#pod
#pod requires 'dir';
#pod
#pod test thing_has_unix_dir => sub {
#pod my ($self) = @_;
#pod
#pod my $dir = $self->dir;
#pod like($dir, qr{\A(?:/\w+)+/?\z}, "thing has a unix-like directory");
#pod };
#pod }
#pod
#pod # We might have one class that is only expected to pass one test:
#pod {
#pod package JustHasID;
#pod use Moose;
#pod
#pod has id => (
#pod is => 'ro',
#pod default => sub {
#pod my ($self) = @_;
#pod return Scalar::Util::refaddr($self);
#pod },
#pod );
#pod }
#pod
#pod # ...and another class that should pass both:
#pod {
#pod package UnixUser;
#pod use Moose;
#pod
#pod has id => (is => 'ro', default => 501);
#pod has dir => (is => 'ro', default => '/home/users/rjbs');
#pod }
#pod
#pod # So far, none of this is new, it's just a slightly different way of factoring
#pod # things we've seen before. In t/01-demo.t, we wrote distinct test roles and
#pod # classes, and we made our class compose the role explicitly. This can be
#pod # a useful way to put these pieces together, but we also might want to write
#pod # all these classes and roles as unconnected components and compose them only
#pod # when we're ready to run our tests. When we do that, we can tell run_tests
#pod # what to put together.
#pod #
#pod # Here, we tell it that we can test JustHasID with Test::ThingHasID:
#pod run_tests(
#pod "our JustHasID objects have ids",
#pod [ 'JustHasID', 'Test::ThingHasID' ],
#pod );
#pod
#pod # ...but we can run two test routines against our UnixUser class
#pod run_tests(
#pod "unix users have dirs and ids",
#pod [ 'UnixUser', 'Test::ThingHasID', 'Test::HasDirectory' ],
#pod );
#pod
#pod
#pod # We can still use the "attributes to initialize an object," and when doing
#pod # that it may be that we don't care to run all the otherwise applicable tests,
#pod # because they're not interesting in the scenario we're creating. For
#pod # example...
#pod run_tests(
#pod "a trailing slash is okay in a directory",
#pod [ 'UnixUser', 'Test::HasDirectory' ],
#pod { dir => '/home/meebo/' },
#pod );
#pod
#pod # ...and we're done!
#pod done_testing;
#pod
#pod
#pod
#pod
#pod =cut
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
Test::Routine::Manual::Demo - a walkthrough, in code, of Test::Routine
=head1 VERSION
version 0.020
=head1 The Demo
=head2 t/demo/01-demo.t
#!/bin/env perl
use strict;
use warnings;
# This test is both a test and an example of how Test::Routine works! Welcome
# to t/01-demo.t, I will be your guide, rjbs.
{
# This block defines the HashTester package. It's a Test::Routine, meaning
# it's a role. We define state that the test will need to keep and any
# requirements we might have.
#
# Before we can run this test, we'll need to compose the role into a class so
# that we can make an instance.
package HashTester;
use Test::Routine;
# We import stuff from Test::More because, well, who wants to re-write all
# those really useful test routines that exist out there? Maybe somebody,
# but not me.
use Test::More;
# ...but then we use namespace::autoclean to get rid of the routines once
# we've bound to them. This is just standard Moose practice, anyway, right?
use namespace::autoclean;
# Finally, some state! Every test will get called as method on an instance,
# and it will have this attribute. Here are some points of interest:
#
# - We're giving this attribute a builder, so it will try to get built with a
# call to $self->build_hash_to_test -- so each class that composes this
# role can provide means for these attributes (fixtures) to be generated as
# needed.
#
# - We are not adding "requires 'build_hash_to_test'", because then we can
# apply this role to Moose::Object and instantiate it with a given value
# in the constructor. There will be an example of this below. This lets
# us re-use these tests in many variations without having to write class
# after class.
#
# - We don't use lazy_build because it would create a clearer. If someone
# then cleared our lazy_build fixture, it could not be re-built in the
# event that we'd gotten it explicitly from the constructor!
#
# Using Moose attributes for our state and fixtures allows us to get all of
# their powerful behaviors like types, delegation, traits, and so on, and
# allows us to decompose shared behavior into roles.
#
has hash_to_test => (
is => 'ro',
isa => 'HashRef',
builder => 'build_hash_to_test',
);
# Here, we're just declaring an actual test that we will run. This sub will
# get installed as a method with a name that won't get clobbered easily. The
# method will be found later by run_tests so we can find and execute all
# tests on an instance.
#
# There is nothing magical about this method! Calling this method is
# performed in a Test::More subtest block. A TAP plan can be issued with
# "plan", and we can issue TODO or SKIP directives the same way. There is
# none of the return-to-skip magic that we find in Test::Class.
#
# The string after "test" is used as the method name -- which means we're
# getting a method name with spaces in it. This can be slightly problematic
# if you try to use, say, ::, in a method name. For the most part, it works
# quite well -- but look at the next test for an example of how to give an
# explicit description.
test "only one key in hash" => sub {
my ($self) = @_;
my $hash = $self->hash_to_test;
is(keys %$hash, 1, "we have one key in our test hash");
is(2+2, 4, "universe still okay");
};
# The only thing of note here is that we're passing a hashref of extra args
# to the test method constructor. "desc" lets us set the test's description,
# which is used in the test output, so we can avoid weird method names being
# installed. Also note that we order tests more or less by order of
# definition, not by name or description.
test second_test => { desc => "Test::Routine demo!" } => sub {
pass("We're running this test second");
pass("...notice that the subtest's label is the 'desc' above");
pass("...and not the method name!");
};
}
{
# This package is one fixture against which we can run the HashTester
# routine. It has the only thing it needs: a build_hash_to_test method.
# Obviously real examples would have more to them than this.
package ProcessHash;
use Moose;
with 'HashTester';
use namespace::autoclean;
sub build_hash_to_test { return { $$ => $^T } }
}
# Now we're into the body of the test program: where tests actually get run.
# We use Test::Routine::Util to get its "run_tests" routine, which runs the
# tests on an instance, building it if needed.
use Test::Routine::Util;
# We use Test::More to get done_testing. We don't assume that run_tests is the
# entire test, because that way we can (as we do here) run multiple test
# instances, and can intersperse other kinds of sanity checks amongst the
# Test::Routine-style tests.
use Test::More;
is(2+2, 4, "universe still makes sense") or BAIL_OUT("PANIC!");
# The first arg is a description for the subtest that will be run. The second,
# here, is a class that will be instantiated and tested.
run_tests('ProcessHash class' => 'ProcessHash');
# Here, the second argument is an instance of a class to test.
run_tests('ProcessHash obj' => ProcessHash->new({ hash_to_test => { 1 => 0 }}));
# We could also just supply a class name and a set of args to pass to new.
# The below is very nearly equivalent to the above:
run_tests('ProcessHash new' => ProcessHash => { hash_to_test => { 1 => 0 }});
# ...and here, the second arg is not a class or instance at all, but the
# Test::Routine role (by name). Since we know we can't instantiate a role,
# run_tests will try to compose it with Moose::Object. Then the args are used
# as the args to ->new on the new class, as above. This lets us write
# Test::Routines that can be tested with the right state to start with, or
# Test::Routines that need to be composed with testing fixture classes.
run_tests(
'HashTester with given state',
HashTester => {
hash_to_test => { a => 1 },
},
);
# There's one more interesting way to run out tests, but it's demonstrated in
# 02-simple.t instead of here. Go check that out.
# ...and we're done!
done_testing;
=head2 t/demo/02-simple.t
# Welcome to part two of the Test::Routine demo. This is showing how you can
# write quick one-off tests without having to write a bunch of .pm files or
# (worse?) embed packages in bare blocks in the odious way that 01-demo.t did.
#
# First off, we use Test::Routine. As it did before, this turns the current
# package (main!) into a Test::Routine role. It also has the pleasant
# side-effect of turning on strict and warnings.
use Test::Routine;
# Then we bring in the utils, because we'll want to run_tests later.
use Test::Routine::Util;
# And, finally, we bring in Test::More so that we can use test assertions, and
# namespace::autoclean to clean up after us.
use Test::More;
use namespace::autoclean;
# We're going to give our tests some state. It's nothing special.
has counter => (
is => 'rw',
isa => 'Int',
default => 0,
);
# Then another boring but useful hunk of code: a method for our test routine.
sub counter_is_even {
my ($self) = @_;
return $self->counter % 2 == 0;
}
# Then we can write some tests, just like we did before. Here, we're writing
# several tests, and they will be run in the order in which they were defined.
# You can see that they rely on the state being maintained.
test 'start even' => sub {
my ($self) = @_;
ok($self->counter_is_even, "we start with an even counter");
$self->counter( $self->counter + 1);
};
test 'terminate odd' => sub {
my ($self) = @_;
ok(! $self->counter_is_even, "the counter is odd, so state was preserved");
pass("for your information, the counter is " . $self->counter);
};
# Now we can run these tests just by saying "run_me" -- rather than expecting a
# class or role name, it uses the caller. In this case, the calling package
# (main!) is a Test::Routine, so the runner composes it with Moose::Object,
# instantiating it, and running the tests on the instance.
run_me;
# Since each test run gets its own instance, we can run the test suite again,
# possibly to verify that the test suite is not destructive of some external
# state.
run_me("second run");
# And we can pass in args to use when constructing the object to be tested.
# Given the tests above, we can pick any starting value for "counter" that is
# even.
run_me({ counter => 192 });
# ...and we're done!
done_testing;
# More Test::Routine behavior is demonstrated in t/03-advice.t and t/04-misc.t
# Go have a look at those!
=head2 t/demo/03-advice.t
use Test::Routine;
use Test::Routine::Util;
use Test::More;
use namespace::autoclean;
# xUnit style testing has the idea of setup and teardown that happens around
# each test. With Test::Routine, we assume that you will do most of this sort
# of thing in your BUILD, DEMOLISH, and attribute management. Still, you can
# easily do setup and teardown by applying method modifiers to the "run_test"
# method, which your Test::Routine uses to run each test. Here's a simple
# example.
# We have the same boring state that we saw before. It's just an integer that
# is carried over between tests.
has counter => (
is => 'rw',
isa => 'Int',
lazy => 1,
default => 0,
clearer => 'clear_counter',
);
# The first test changes the counter's value and leaves it changed.
test test_0 => sub {
my ($self) = @_;
is($self->counter, 0, 'start with counter = 0');
$self->counter( $self->counter + 1);
is($self->counter, 1, 'end with counter = 1');
};
# The second test assumes that the value is the default, again. We want to
# make sure that before each test, the counter is reset, but we don't want to
# tear down and recreate the whole object, because it may have other, more
# expensive resources built.
test test_1 => sub {
my ($self) = @_;
is($self->counter, 0, 'counter is reset between tests');
};
# ...so we apply a "before" modifier to each test run, calling the clearer on
# the counter. When next accessed, it will re-initialize to zero. We could
# call any other code we want here, and we can compose numerous modifiers
# together onto run_test.
#
# If you want to clear *all* the object state between each test... you probably
# want to refactor.
before run_test => sub { $_[0]->clear_counter };
run_me;
done_testing;
=head2 t/demo/04-misc.t
use Test::Routine;
use Test::Routine::Util;
use Test::More;
use namespace::autoclean;
# One thing that the previous examples didn't show was how to mark tests as
# "skipped" or "todo." Test::Routine makes -no- provisions for these
# directives. Instead, it assumes you will use the entirely usable mechanisms
# provided by Test::More.
# This is a normal test. It is neither skipped nor todo.
test boring_ordinary_tests => sub {
pass("This is a plain old boring test that always passes.");
pass("It's here just to remind you what they look like.");
};
# To skip a test, we just add a "skip_all" plan. Because test methods get run
# in subtests, this skips the whole subtest, but nothing else.
test sample_skip_test => sub {
plan skip_all => "these tests don't pass, for some reason";
is(6, 9, "I don't mind.");
};
# To mark a test todo, we just set our local $TODO variable. Because the test
# is its own block, this works just like it would in any other Test::More test.
test sample_todo_test => sub {
local $TODO = 'demo of todo';
is(2 + 2, 5, "we can bend the fabric of reality");
};
run_me;
done_testing;
=head2 t/demo/05-multiple.t
#!/bin/env perl
use strict;
use warnings;
use Test::Routine::Util;
use Test::More;
# One of the benefits of building our sets of tests into roles instead of
# classes is that we can re-use them in whatever combination we want. We can
# break down sets of tests into bits that can be re-used in different cases.
# With classes, this would lead to multiple inheritance or other monstrosities.
# Here's a first Test::Routine. We use it to make sure that one of our
# fixture's attributes is a numeric id.
{
package Test::ThingHasID;
use Test::Routine;
use Test::More;
requires 'id';
test thing_has_numeric_id => sub {
my ($self) = @_;
my $id = $self->id;
like($id, qr/\A[0-9]+\z/, "the thing's id is a string of ascii digits");
};
}
# A second one ensures that the thing has an associated directory that
# looks like a unix path.
{
package Test::HasDirectory;
use Test::Routine;
use Test::More;
requires 'dir';
test thing_has_unix_dir => sub {
my ($self) = @_;
my $dir = $self->dir;
like($dir, qr{\A(?:/\w+)+/?\z}, "thing has a unix-like directory");
};
}
# We might have one class that is only expected to pass one test:
{
package JustHasID;
use Moose;
has id => (
is => 'ro',
default => sub {
my ($self) = @_;
return Scalar::Util::refaddr($self);
},
);
}
# ...and another class that should pass both:
{
package UnixUser;
use Moose;
has id => (is => 'ro', default => 501);
has dir => (is => 'ro', default => '/home/users/rjbs');
}
# So far, none of this is new, it's just a slightly different way of factoring
# things we've seen before. In t/01-demo.t, we wrote distinct test roles and
# classes, and we made our class compose the role explicitly. This can be
# a useful way to put these pieces together, but we also might want to write
# all these classes and roles as unconnected components and compose them only
# when we're ready to run our tests. When we do that, we can tell run_tests
# what to put together.
#
# Here, we tell it that we can test JustHasID with Test::ThingHasID:
run_tests(
"our JustHasID objects have ids",
[ 'JustHasID', 'Test::ThingHasID' ],
);
# ...but we can run two test routines against our UnixUser class
run_tests(
"unix users have dirs and ids",
[ 'UnixUser', 'Test::ThingHasID', 'Test::HasDirectory' ],
);
# We can still use the "attributes to initialize an object," and when doing
# that it may be that we don't care to run all the otherwise applicable tests,
# because they're not interesting in the scenario we're creating. For
# example...
run_tests(
"a trailing slash is okay in a directory",
[ 'UnixUser', 'Test::HasDirectory' ],
{ dir => '/home/meebo/' },
);
# ...and we're done!
done_testing;
=head1 AUTHOR
Ricardo Signes <rjbs@cpan.org>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2010 by Ricardo Signes.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut