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;
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;
}
Related
There are lots of material talking about writing coverage on module but how if I want to write a test for command runnable perl script (main.pl)?
Is there a need to write test for main.pl or I just need to write test for module will do?
Let's say I have these two scripts.
command runnable script
main.pl
import Halo;
&main;
sub main() {
my $a = 2;
my $b = 3;
my $c = Halo.add($a, $b);
print "a + b = $c\n";
}
==============================================
Perl module
Halo.pm
package Halo;
sub add() {
my ($class, $a, $b) = #_;
return $a + $b;
}
1;
==============================================
Run in command line:
perl main.pl
I suggest you research "unit" and "end-to-end" testing to understand the benefits of each. I would recommend that you do both. Testing your main script is easily done with "modulinos", allowing you to fairly painlessly tie into the existing Perl testing ecosystem.
From the synopsis: https://metacpan.org/pod/Devel::Cover#SYNOPSIS
$ perl -I. -MDevel::Cover main.pl
$ cover
You'll also need to change your code a bit. You'll need "use" instead of "import" and "Halo->add" instead of "Halo.add". (These changes are nothing to do with Devel::Cover.)
I am trying to find if it is possible to run just one test case in a Perl test file. For example, consider the following Perl test file (copied from http://www.thegeekstuff.com/2013/02/perl-test-simple/):
#!/usr/bin/perl
use strict;
use warnings;
use Test::Simple tests => 2;
sub hello_world
{
return "Hello world!";
}
sub get_number
{
return int(rand(1000));
}
ok( hello_world( ) eq "Hello world!", "My Testcase 1" );
ok( get_number( ) > 0, "My Testcase 2" );
When I run this file using command prove -v test.t I get following output:
test.t ..
1..2
ok 1 - My Testcase 1
ok 2 - My Testcase 2
ok
Now, I just want to run the 2nd test case i.e. My Testcase2. Is there a way to execute just one named test case in a test file?
The short answer is: No, with the simple test framework provided by Test::Simple (or Test::More) and the prove command there is no way to single out one specific assertion from a test script and run just that assertion.
However the Perl testing ecosystem has a rich and diverse set of options available. For example Test::Class allows you to add an object oriented layer on top of the standard test protocol. This allows you to group tests into methods, add setup and teardown methods, and also to run individual test methods. You still won't be able to do it all from the command-line without modifying your test script but it's a step closer to what you're asking for.
Of course a much simpler solution would be to copy out just the the bit you want into its own .t file and run that.
I have the usual MakeMaker module with a t/ tests directory, and I can run a single test file with e.g., prove -I lib t/my-test.t.
My tests use Test::Class and Test::More and subs (with the modulino technique from Effective Perl), like this:
use strict;
use warnings;
use base 'Test::Class';
use Test::More;
__PACKAGE__->runtests() unless caller;
sub set_up : Test(setup) {
# ...
}
sub test_something : Test {
is(MyModule::some_sub(1), 1);
}
# ...more test subs...
Now I want to use the Perl debugger to investigate a test sub that shows a problem in my module. I want to only run test_something in the debugger, without running all the other test subs in the .t file.
prove does not seem to have such an option.
perl -d -I lib t/my-test.t runs all tests, unless I change my modulino to call the setup method and then the actual test method instead of __PACKAGE__->runtests():
unless (caller) {
set_up();
test_something();
done_testing();
}
How can I run only one test sub without modifying the code?
To avoid running all of your tests, caller must be defined when your test script is loaded. Try something like this:
$ perl -Ilib -de 1
DB<1> do 't/my-test.t'
DB<2> set_up()
DB<3> b test_something_else
DB<4> test_something_else()
... step through test_something_else() function ...
DB<16> done_testing()
Reading through the Dancer::Test documentation made it seem straightforward to do a test, but I'm missing something. If I have the following Dancer application (WebApp.pm):
package WebApp;
use Dancer;
# declare routes/actions
get '/' => sub {
"Hello World";
};
dance;
and then the following testing file 001_base.t:
use strict;
use warnings;
use Test::More tests => 1;
use WebApp;
use Dancer::Test;
response_status_is [GET => '/'], 200, "GET / is found";
Then when I run the test: perl 001_base.t, the output is that the dancer script starts up:
Dancer 1.3132 server 7679 listening on http://0.0.0.0:3000
== Entering the development dance floor ...
But then waits. (This is the same as if I just run the code in WebApp.pm). What am I missing here? I guess I'm not running the test correctly.
You should remove dancer() from the WebApp.pm. Here is the correct content:
package WebApp;
use Dancer;
# declare routes/actions
get '/' => sub {
"Hello World";
};
1;
Then you test will pass.
The common way to create dancer apps is to declare all the routes in one or more .pm files and to have a file usually called app.psgi with the content:
#!/usr/bin/env perl
use Dancer;
use WebApp;
dance;
Then to start your web application you should run perl -Ilib app.psgi.
Here is the scenario, I have two files:
1. dir/A.pm
2. dir/new_dir/a.t
This is how A.pm looks like:
package A;
use FindBin;
use Test::More;
is (FindBin->again, 'dir', 'got dir');
1;
This is how a.t looks like:
use FindBin;
use Test::More qw(no_plan);
use A;
is (FindBin->again, 'dir/new_dir', 'got dir/new_dir');
So I ran file a.t with perl new_dir/a.t and expect my tests to pass. But this is my test result:
not ok 1 - got dir
# Failed test 'got fir'
# at A.pm line 6.
# got: 'dir/new_dir'
# expected: 'dir'
ok 2 - got dir/new_dir
1..2
Am I doing anything wrong? I am very new to perl. Please help!!
As Dave Sherohman notes, FindBin is for finding the location of the main script, not individual modules. From the documentation:
NAME
FindBin - Locate directory of original perl script
(Admittedly, the documentation does, somewhat confusingly, refer to "modules" in the "KNOWN ISSUES" section, but it doesn't really mean what you think it means by that.)
Anyway, if you look at the source with perldoc -m FindBin, you'll see that FindBin obtains the path to the script from the $0 variable. If you're interested in finding the location of a module included via use (or require), you should look under %INC instead, something like this:
package Foo::Bar;
use File::Spec;
my ($vol, $dir, $file) = File::Spec->splitpath( $INC{'Foo/Bar.pm'} );
FindBin finds the location of the file Perl launched, not of file currently executing.
I don't see why you'd need the path to a module — File::ShareDir can be used to access your module's data files — but the following will find it:
use Cwd qw( realpath );
use File::Basename qw( dirname );
my $module_dir = dirname(realpath(__FILE__));
The same caveat as Find::Bin applies: This only works if chdir hasn't been changed.
If I understand the question correctly, a.t is in the directory dir/new_dir/ and you're using new_dir/a.t to run it from dir/, right?
If so, then it is doing the right thing. Since a.t is in dir/new_dir, you should always get dir/new_dir from FindBin - its job is to Find the Binary (program), not to find the file it's being called from, so the result will be the same in A.pm as it is in a.t.
The ->again function is for running instances of completely different programs from within the same perl interpreter, such as what mod_perl does, not for just using different modules within a single program.