t3o
t3o

Reputation: 329

How can I test subs in a perl programs with Test::More without losing the programs exit code?

When writing small to medium size perl programs I have functions that I would like to test with "Test::More".

The idea is to be able to call my program like "./supertool.pl --test" to do the tests. While beeing able to use "./supertool.pl" for normal program usage.

But when I add "use Test::More qw(no_plan);" in the head section it garbles the program output and its exit code.

Here's an example which can be called in console like: './supertool.pl ; echo $? ; echo "Expected: 0"'.

#!/usr/bin/perl

use strict;
use warnings;
use Test::More qw(no_plan);

if (0)
{
        note("one test");
        test__x();
        done_testing;
}

exit(0);

sub test_x
{
        ok( 1, "1 is ok" );
}

The output is:

1..0
255
Expected: 0

But I whished the output to be

0
Expected: 0

Is it possible to tweak Test::More into not printing "1..0" and into not garbling the programs exit code when doing no tests?

Upvotes: 3

Views: 406

Answers (1)

zdim
zdim

Reputation: 66899

There are several ways to get the behavior you want.

One way is to tell it that you won't run tests, per that command-line option

use warnings;
use strict;
use feature 'say';

use Test::More;

my $run_tests = shift;

if ($run_tests) { 
    Test::More->builder->no_plan;
} 
else {
    Test::More->builder->output('/dev/null'); 
    Test::More->builder->skip_all('Not testing');
}
#...

if ($run_tests) {
    note("one test");
    test_x();
}

sub test_x { ok( 1, "1 is ok" ) }

The message for skipping won't be seen as the output is redirected to /dev/null, as desired, but is provided as an example (and there must be something there).

I use methods from the backend Test::Builder module (Test::More->builder returns its object) to set a plan/skip, even as there is a plan function in Test::More itself.

This is for consistency, since for output we do need Test::Builder, and as further examples of Test::Builder, where one can find many more tools in cases where that may be needed. The Test::More is built upon this moduel and I suggest a good perusal of it.

An alternative to declaring no_plan is to use done_testing at the very end of the program, conditioned on $run_tests (but not in END block, see docs).

Another way is to place all tests in SKIP blocks, used for conditional testing

use warnings;
use strict;
use feature 'say';

use Test::More qw(no_plan);

my $run_tests = shift; 
    
SKIP: {
    skip_test() if not $run_test;

    note("one test");
    test_x();
}


sub test_x { ok( 1, "1 is ok" ) }

sub skip_test {
    Test::More->builder->output('/dev/null'); 
    skip "Not testing";
}

where output is again sent to /dev/null, as desired in the question.

We can also use Test::Builder methods directly, either skip

use warnings;
use strict;
use feature 'say';    
use Test::More qw(no_plan);

my $run_tests = shift;

if ($run_tests) {
    note("one test");
    test_x();
}
else { skip_test() }


sub test_x { ok( 1, "1 is ok" ) } 

sub skip_test {
    Test::More->builder->output('/dev/null');
    Test::More->builder->skip;
}

or perhaps reset, for different circumstances or context

use warnings;
use strict;
use feature 'say';

use Test::More qw(no_plan);

my $run_tests = shift;

if ($run_tests) {
    note("one test");
    test_x();
}
else { Test::More->builder->reset }
    
# At the very end of the program (not in END block though)
done_testing if $run_tests;

sub test_x { ok( 1, "1 is ok" ) } 

where we're now best off using done_testing in the end.

All of these print nothing and have exit code 0 when the program is invoked without any arguments, or run tests and print messages when invoked with a "true" argument. All of them were tested with more (than 1) testing blocks, omitted here for brevity.

The need to deal with some details above comes up because the testing framework is intended to be used in dedicated programs, not for testing pieces in a running program. And while it is possible to use it this way as well some things get more complicated.

See Test2, or perhaps its suite Test2::Suite, for a newer alternative.


That is, "true" for Perl when passed to the program, so not 0 or empty string "''"

Upvotes: 3

Related Questions