File: 01-demo.t

package info (click to toggle)
libtest-routine-perl 0.031-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 296 kB
  • sloc: perl: 705; makefile: 2
file content (146 lines) | stat: -rw-r--r-- 6,019 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#!/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;