The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Test2::Tools::Spec - SPEC testing tools

*** EXPERIMENTAL ***

This distribution is experimental, anything can change at any time!

DESCRIPTION

Tools for writing SPEC style tests.

SYNOPSIS

    use Test2::Bundle::Extended;
    use Test2::Tools::Spec;

    describe fruit_shipment => sub {
        my $crates;
        before_all unload_crates => sub { $crates = get_crates() };
        after_all deliver_crates => sub { deliver($crates) };

        my $fruit;
        for my $f ($crates->types) { # 'pear' and 'apple'
            case $f => sub { $fruit = $f };
        }

        my $crate;
        before_each open_crate => sub { $crate = $crates->open_first($fruit) };
        after_each close_crate => sub { $crates->store($crate) };

        tests squishability => sub {
            my $sample = $crate->grab();
            ok($sample->squishability > 5, "squish rating is greater than 5");
            ok($sample->squishability < 10, "squish rating is less than 10");
        };

        tests flavor => sub {
            my $sample = $crate->grab();
            ok($sample->is_tasty, "sample is tasty");
            ok(!$sample->is_sour, "sample is not sour");
        };

        tests ripeness => sub {
            my $sample1 = $crate->grab();
            my $sample2 = $crate->grab();

            my $overripe  = grep { $_->is_overripe }  $sample1, $sample2;
            my $underripe = grep { $_->is_underripe } $sample1, $sample2;

            ok($overripe  < 2, "at least 1 sample is not overripe");
            ok($underripe < 2, "at least 1 sample is not underripe");
        };
    };

    done_testing;

In this sample we are describing a fruit shipment. Before anything else we unload the crates. Next we handle 2 types of fruit, a crate of pears and a crate of apples. For each create we need to run tests on squishability, flavor, and ripeness. In order to run these tests the creates need to be opened, when the tests are done the crates need to be closed again.

We use the before_all and after_all to specify the first and last tasks to be run, each one will run exactly once. The 3 sets of tests will be run once per fruit type, we have cases for pears and apples, so in total there will be 6 sets of tests run, 3 per fruit type. Opening and closing the crate is something we need to do for each test block, so we use before_each and after_each.

Each test block needs unique samples, so the sample is aquired within the test. We do not use a before_each as some tests require different numbers of samples.

IMPORT OPTIONS

    use Test2::Tools::Spec -opt => 'val', qw/tests describe .../;

Any argument prefixed with a '-' is considered an option, it and the following argument will be used as arguments to the runner construction (with '-' stripped off). All arguments without the '-' prefix will be considered imports.

-verbose => $BOOL

Turns on some extra verbosity in debug messages.

-subtests => $BOOL

Normally all spec units are run as subtests. This can be used to turn off the behavior by setting it to 0. Note: Test2::Workflow::Runner::Isolate and subclasses currently ignore this flag.

-rand => $BOOL

Normally spec units are run in random order. This can be used to turn that behavior off by setting it to 0.

-max => $INT

Maximum number of async units to run at a time, default is 3. When the T2_WORKFLOW_ASYNC environment variable is also set, the lowest value will be used.

This is only effective when an isolating runner is used.

-no_fork => $BOOL

Specify that isolating runners should NOT use forking for isolation purposes.

This is only effective when an isolating runner is used.

-no_threads => $BOOL

Specify that isolating runners should NOT use threads for isolation purposes.

This is only effective when an isolating runner is used.

EXPORTS

All exports have the exact same syntax, there are 2 forms:

    FUNCTION($NAME, \&CODE);
    FUNCTION($NAME, \%PARAMS, \&CODE);

Both can also be used in a style that is more pleasingto the eye:

    FUNCTION $NAME => sub { ... };
    FUNCTION $NAME => {...}, sub { ... }

The name and codeblock are required. Optionally you can provide a hashref of parameters between the name and coderef with parameters. Valid parameters depends on what runner is used, but the parameters supported by default are:

Note: The default runner is Test2::Workflow::Runner.

todo => 'reason'

This will mark the entire block as todo with the given reason. This parameter is inherited by nested blocks.

skip => 'reason'

This will skip the entire block, it will generate a single 'Ok' event with the skip reason set.

mini => $bool

This tells the workflow system that the specified block is not significant enough to need its own subtest or final 'ok'. This will basically cause all its output to be inlined into the parent subtest. Use this if you have a nested subtest with only 1 result, or where the additional subtest indentation adds noise instead of helping.

This is completely aesthetic, it has no effect on if your tests will pass or fail. This is here for humans, not machines.

async => $bool

Test block can run concurrently with other tests in another process or thread. This does not mean it will, just that it may be.

Using this option will upgrade the runner to an Test2::Workflow::Runner::Isolate subclass.

iso => $bool

Test block MUST be run in an isolated process or thread.

Using this option will upgrade the runner to an Test2::Workflow::Runner::Isolate subclass.

Note: The tests you declare are deferred, that is they run after everything else is done, typically when you call done_testing.

TEST DECLARATIONS

Test declarations are used to declare blocks of tests. You can put pretty much anything you want inside a test block, the only exceptions to this is that other test blocks, groups, modifiers, etc, cannot be specified inside the test block.

If a test block does not produce any events then it will be considered an error. Test blocks are usually run as subtests.

tests $name => sub { ... }
it $name => sub { ... }
tests $name => \%params, sub { ... }
it $name => \%params, sub { ... }

tests() and it() are both aliases to the same function. The name tests() is present as the authors preference. it() is present to reflect the name used in RSPEC for the Ruby programming language.

Note: The subs get no arguments, and their return is ignored.

mini $name => sub { ... }
mini $name => \%params, sub { ... }

Same as tests and it except that mini => 1 is added to \%params.

async $name => sub { ... }
async $name => \%param, sub { ... }

Shortcut, adds the async => 1 parameter.

iso $name => sub { ... }
iso $name => \%params, sub { ... }

Shortcut, adds the iso => 1 parameter.

masync $name => sub { ... }
masync $name => \%params, sub { ... }

Shortcut, adds the async => 1, mini => 1 parameters.

miso $name => sub { ... }
miso $name => \%params, sub { ... }

Shortcut, adds the iso => 1, mini => 1 parameters.

TEST SETUP AND TEARDOWN

These blocks attach themselves to test blocks. The setup/teardown will run once for each test block. These are all inherited by test blocks declared in nested groups.

before_each $name => sub { ... }

Declare a setup that will run before each test block is run. Note: This will run before any modifier.

Note: The subs get no arguments, and their return is ignored.

after_each $name => sub { ... }

Declare a teardown that will run after each test block.

Note: The subs get no arguments, and their return is ignored.

around_each $name => sub { ... }

Declare a setup+teardown that is wrapped around the test block. This is useful if you want to localize a variable, or something similar.

    around_each foo => sub {
        my $inner = shift;

        local %ENV;

        # You need to call the 'inner' sub.
        $inner->();
    };

Note: The subs get no arguments, and their return is ignored.

TEST MODIFIERS

case $name => sub { ... }

You can specify any number of cases that should be used. All test blocks are run once per case. Cases are inherited by nested groups.

Note: The subs get no arguments, and their return is ignored.

TEST MODIFIER SETUP AND TEARDOWN

before_case $name => sub { ... }

Code to be run just before a case is run.

Note: The subs get no arguments, and their return is ignored.

after_case $name => sub { ... }

Code to be run just after a case is run (but before the test block).

Note: The subs get no arguments, and their return is ignored.

around_case $name => sub { ... }

Code that wraps around the case.

    around_case foo => sub {
        my $inner = shift;

        local %ENV;

        # You need to call the 'inner' sub.
        $inner->();
    };

Note: The subs get no arguments, and their return is ignored.

TEST GROUPS

describe $name => sub { ... }
cases $name => sub { ... }

describe() and cases() are both aliases to the same thing.

These can be used to create groups of test block along with setup/teardown subs. The cases, setups, and teardowns will not effect test blocks outside the group. All cases, setups, and teardown will be inherited by any nested group.

Note: Group subs are run as they are encountered, unlike test blocks which are run at the very end of the test script.

Note: The subs get no arguments, and their return is ignored.

GROUP MODIFIERS

before_all $name => sub { ... }

Specify a setup that gets run once at the start of the test group.

Note: The subs get no arguments, and their return is ignored.

after_all $name => sub { ... }

Specify a teardown that gets run once at the end of the test group.

Note: The subs get no arguments, and their return is ignored.

around_all $name => sub { ... }

Specify a teardown that gets run once, around the test group.

    around_all foo => sub {
        my $inner = shift;

        local %ENV;

        # You need to call the 'inner' sub.
        $inner->();
    };

Note: The subs get no arguments, and their return is ignored.

NOTE ON RUN ORDER

Within a test group (the main package counts as a group) things run in this order:

group blocks (describe, cases)
END OF SCRIPT (done_testing called)
before_all + around_all starts
before_each + around_each starts
before_case + around_case starts
case
after_case + around_case stops
tests/it
after_each + around_each stops
after_all + around_all stops

SOURCE

The source code repository for Test2-Workflow can be found at http://github.com/Test-More/Test2-Workflow/.

MAINTAINERS

Chad Granum <exodist@cpan.org>

AUTHORS

Chad Granum <exodist@cpan.org>

COPYRIGHT

Copyright 2015 Chad Granum <exodist7@gmail.com>.

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

See http://dev.perl.org/licenses/