=encoding UTF-8

=head1 NAME

Test2::Tools::xUnit - Perl xUnit framework built on Test2::Workflow

=for html
<a href=https://travis-ci.org/cv-library/Test2-Tools-xUnit>
    <img src=https://api.travis-ci.org/cv-library/Test2-Tools-xUnit.svg>
</a>
<a href=https://coveralls.io/r/cv-library/Test2-Tools-xUnit>
    <img src=https://coveralls.io/repos/cv-library/Test2-Tools-xUnit/badge.svg>
</a>
<a href=https://metacpan.org/pod/Test2::Tools::xUnit>
    <img src=https://badge.fury.io/pl/Test2-Tools-xUnit.svg>
</a>
<a href=https://github.com/cv-library/Test2-Tools-xUnit/issues>
    <img src=https://img.shields.io/github/issues/cv-library/Test2-Tools-xUnit.svg>
</a>

=head1 SYNOPSIS

 use Test2::Tools::xUnit;
 use Test2::V0;

 use DBI;
 use Local::SystemUnderTest; # $self->{sys} in this example.

 # Tests are indicated with the :Test attribute.  You can make multiple
 # assertions per test if desired.  Each method is called on a new instance.
 sub run_without_args : Test {
    my $self = shift;
    ok $self->{sys}->run, 'should return true';
 }

 # Skip tests if you don't want them to be run.
 sub self_destruct : Test Skip(unwanted side effects) {
    my $self = shift;
    ok !lives { $self->{sys}->self_destruct() }, "should die";
 }

 # Todo tests will be run; failures are reported and ignored.
 sub frobnicate : Test Todo(not yet implemented) {
    my $self = shift;
    is $self->{sys}->frobnicate(), 42, 'should return 42';
 }

 # BeforeEach/AfterEach are called once per test method, on an instance.
 my $dbh;
 sub before_each : BeforeEach {
    my $self = shift;
    $self->{sys} = Local::SystemUnderTest->new( dbh => $dbh );
 }

 sub after_each : AfterEach {
    my $self = shift;
    $dbh->do("DELETE FROM log");
 }

 # BeforeAll/AfterAll are called once, as class methods.
 sub before_all : BeforeAll {
    $dbh = DBI->connect(...);
 }

 sub after_all : AfterAll {
    $dbh->disconnect;
 }

 done_testing;

=head1 DESCRIPTION

Test2::Tools::xUnit is an implementation of xUnit for Perl that works with
L<Test2>.  It uses subroutine attributes to indicate which methods should be
called at which times.

By constructing a separate object per test, and randomizing the order in
which the tests are called, Test2::Tools::xUnit attempts to provide strong
isolation between test methods.

=head1 SUBROUTINE ATTRIBUTES

=over

=item Test

C<Test> indicates that a method should be run as a test.

Any number of assertions can be made within a test method.

=item Skip(<reason>)

C<Skip> will cause a test method not to be run, but it will be reported as
"skipped" in the output.

If omitted, the reason defaults to the name of the test method.

=item Todo(<reason>)

C<Todo> will cause a test method to be run, but any failure will be
reported and ignored.

If omitted, the reason defaults to the name of the test method.

=item BeforeEach

C<BeforeEach> methods will be run before each test method.  They are invoked
with the test instance as the first argument, so can store fixtures in the
object:

 sub before_each : BeforeEach {
     my $self = shift;
     $self->{srv} = Local::Service->new(
         ua => $mock_ua,
     );
 }

 sub service_run_with_foo_bar : Test {
     my $self = shift;
     my $res = $self->{srv}->run(foo => 'bar');

     # ...make assertions about the response...
 }

=item AfterEach

C<AfterEach> methods will be run after each test method.

=item BeforeAll

C<BeforeAll> methods will be run once before any tests are started; it runs
as a class method before any test instances are constructed, so cannot
modify any instance attributes.  It can, however, modify package variables,
and these will be available to all tests:

 my $dbh;

 sub before_all : BeforeAll {
     $dbh = DBI->connect(...);
     # ...other db setup...
 }

This is useful for setting up expensive fixtures such as databases, although
you must take care to reset their state between each test; probably in an
C<AfterEach> method.

Because of the risk of breaking test isolation, C<BeforeAll> should be used
sparingly; if C<BeforeEach> would work, use that instead.

=item AfterAll

C<AfterAll> methods will be run after all tests in the class have completed.
Similarly to C<BeforeAll>, this will be invoked as a class method.

 sub after_all : AfterAll {
     $dbh->disconnect;
 }

=back

=head1 FEATURES

=head2 Integration with Test2

Rather than needing a separate test runner, all Test2::Tools::xUnit tests
will be run when C<done_testing> is seen.

Each test method creates a Test2 Subtest.

=head2 Randomization of test order

Test methods within a class are called in a random order, to help avoid tests
accidentally depending on side-effects of other test methods.

Note that if you use L<Test2::Plugin::SRand> (e.g. via L<Test2::V0>), then
the random order will be dependent on the seed chosen.

=head2 Lack of inheritance

Note that your test classes should not inherit from Test2::Tools::xUnit;
importing the module is enough to make the subroutine attributes work.

This behaviour differs from L<Test::Class>, L<Test::Class::Moose> and
older implementations of xUnit in other languages.

=head2 Default object constructor

If your test class has a 'new' method, it will be called to construct
instances of the class.

If no such method exists, Test2::Tools::xUnit will bless a hash into the
calling package for you.  For example, this test passes:

 package Foo;

 use Test2::Tools::xUnit;
 use Test2::V0;
 use Scalar::Util qw(reftype blessed);

 sub called_with_reference_to_blessed_hash : Test {
     my $self = shift;
     is blessed($self), 'Foo';
     is reftype($self), 'HASH';
 }

 done_testing;

=head2 Per-method test instance lifecycle

Test2::Tools::xUnit creates a new object per test method, to promote
isolation of tests.  The following tests both pass, regardless of the order
in which they are called:

 use Test2::Tools::xUnit;
 use Test2::V0;

 sub new { bless { list => [] }, shift }

 sub add_one_to_list : Test {
     my $self = shift;
     push @{$self->{list}}, "one";
     is @{$self->{list}}, 1, "list should have one element";
 }

 sub check_list_is_empty : Test {
     is @{shift->{list}}, 0, "list should be empty";
 }

 done_testing;

This behaviour is shared with JUnit, but differs from some other xUnit
implementations such as NUnit or L<Test::Class>.  For more discussion, see
L<https://martinfowler.com/bliki/JunitNewInstance.html>

=head2 Choice of object framework

Test2::Tools::xUnit will work with your choice of Perl object system, or
none.  Here is a fictitious example using Moo:

 package Calculator::Test;

 use Moo;
 use Test2::Tools::xUnit;
 use Test2::V0;
 use Calculator;

 has 'calc' => ( is => 'ro', default => sub { Calculator->new } );

 sub addition : Test {
     my $result = shift->calc->add(2, 2);
     is $result, 4, "2 + 2 should equal 4";
 }

 done_testing;

The C<default> value of the C<calc> attribute combines with the lifecycle
described above to make a concise alternative to a C<BeforeEach> setup method.

=head1 TESTING ADVICE

Entire books have been written on testing; we cannot reproduce all their
advice here.  However, here are some brief guidelines that might be
relevant specifically to xUnit.

=head2 One test method per behaviour

Avoid naming test methods after the function they are testing, and then
attempting to test all the behaviours in that one method.  You should write
one test method per behaviour.

This guideline doesn't stop you having multiple assertions per behaviour, or
using table-driven tests as in this example:

 use Test2::Tools::xUnit;
 use Test2::V0;

 sub square_root_of_perfect_square : Test {
     my %squares = ( 0 => 0, 1 => 1, 4 => 2, 9 => 3 );
     while (my ($square, $root) = each %squares) {
         my $result = sqrt($square);
         is $result, $root, "should return $root as root of $square";
     }
 }

 sub square_root_of_negative_square : Test {
     my $result = dies { sqrt(-4) };
     is $result, qr/^Can't take sqrt of -4/, "should throw an exception";
 }

 done_testing;

Negative numbers were treated as a separate behaviour here, rather than
attempting to represent the exception as part of the C<%squares> hash.

=head2 Test naming

When failures occur, it helps to have clearly-named tests, to tell you
unambiguously what has gone wrong.

For example, the following failure could be misread as saying an exception
was thrown unexpectedly:

 not ok 1 - square_root_of_negative_square {
     not ok 1 - throws an exception
     1..1
 }

Try to include both the test condition and the expected behaviour in your
test names, and to make use of the word "should" somewhere too.

This produces output which is harder to misread:

 not ok 1 - square_root_of_negative_square {
     not ok 1 - should throw an exception
     1..1
 }

=head2 If you prefer to use many test classes

Test2::Tools::xUnit will work with multiple C<*.t> files, each with
C<done_testing> at the end.  However, some people prefer to adopt a module
structure to share compilation time across your tests, with an approach such
as this:

 # In t/lib/Local/FooBar/Test.pm
 package Local::FooBar::Test;
 use Test2::Tools::xUnit;

 ...

 1;

 # In t/run.t
 use File::Find;
 use Test2::Tools::Basic;

 find({
     wanted => sub {require},
     no_chdir => 1,
 }, './t/lib');

 done_testing;

Extending this example to support running individual test modules is left
as an exercise for the reader.

=head1 COMPATIBILITY

Test2::Tools::xUnit might collide with other Perl modules which handle
subroutine attributes, if both try to create MODIFY_CODE_ATTRIBUTES in the
caller - see L<attributes>.

Currently only Perl 5.12 and higher are supported.

=head1 COPYRIGHT AND LICENSE

Copyright © 2018 CV-Library Ltd.

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

