=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_should_return_true : Test {
    my $self = shift;
    ok $self->{sys}->run, 'run should return true';
 }

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

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

=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.

This allows you to choose to write your tests in multiple C<*.t> files, each
with C<done_testing> at the end.

Alternatively you could 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.

=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 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 test_the_service : 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 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.

