How to avoid "You tried to plan twice" at Test::More
perl, tdd, unit-testing
Solution
The `Test::More` module is built around the TAP format (Test Anything Protocol). The first line of the test output can contain a line that declares the number of tests: `1..12`. This is useful for tools that output the fraction of successful or failed tests: `3/12 tests failed`. However, this line is optional and you can use no plan. In this case it makes sense to say when you're `done_testing`:
use Test::More;
is 1, "1", "stringification";
done_testing;
Note that `use Test::More 'no_plan'` is considered as a plan by Test::More, although it doesn't declare anything in the TAP output.
You should declare a plan (or declare that you're done) only once per process. This is not a problem, as usually testing goes like this:
In your project directory, you have the folders `lib/` with your modules, and `t/` with your tests. Here an example for the `Foo::Bar` module:
./
lib/
Foo/
Bar.pm
Bar/
Helper.pm
t/
00_first_test.t
01_second_test.t
A test looks like this:
#!/usr/bin/env perl
use strict;
use warnings;
use Test::More tests => 1;
# some test here
BEGIN {
use_ok 'Foo::Bar';
}
That is, it preferably has a shebang and declares the number of tests. A package name is not required.
The tests are run with the `prove` tool which ships with Perl. Inside the directory:
$ prove -lr t/
The `-l` includes the `lib` directory, `-r` also looks at subfolders. You can then specify a list of tests or directories containing tests. The tests inside a directory are usually processed alphabetically, but there are options to stir that up.
This will execute a separate process for each test. This is robust, easy to implement and to parallelize, although not terribly performant. This implies that each test is responsible for creating a fixture of it's own.
It is therefore not necessary to call your tests from a central testing script or a makefile.
If you want to execute multiple tests in the same TAP session, you can use subtests:
use Test::More tests => 2;
ok some_test;
subtest "Nice Name" => sub {
plan tests => 1;
ok other_test;
};
Inside the subtest, the plan is declared with the `plan` function, not with `use Test::More` (which would be executed at compile time, before the subtest is executed)! You could structure your test objects to be executed as subtests:
package Tests::Something;
use Test::More;
sub run {
my $self = shift;
plan tests => 2;
ok some_test;
ok other_test;
}
Then:
subtest "Tests::Something" => sub {
Tests::Something->new->run;
}
Problem
I'm new with unit testing. I'm pretending to load N classes that internally uses Test::More in target to make different tests with their own encapsulation. But I'm receveing the error: "You tried to plan twice at Tests/Bar.pm line 9." This method of "more than one test" it's correct one, I mean it's an standard way for doing unit tests in perl? How can I get this level of encapsulation using Test::More? Thanks in advice! main.pl : ``` use strict; use warnings; use utf8; use Tests::Foo; use Tests::Bar; my $ret1 = Tests::Foo->run(); my $ret2 = Tests::Bar->run(); ``` Tests::Foo : ``` package Tests::Foo; use strict; use warnings; sub run { my $ret; use Test::More qw(no_plan); my $test = Test::More->builder; is(1,1,'correct()'); is(1,2,'fails()'); return $test;#return all test object } 1; ``` Tests::Bar ``` package Tests::Bar; use strict; use warnings; sub run { my $ret; use Test::More qw(no_plan); my $test = Test::More->builder; is(2,1,'fail()'); is(2,2,'correct()'); return $test;#return all test object } 1; ```