How can I unit test Perl functions that print to the screen?

perl, testing, unit-testing

Solution

UPDATE: IMHO, the correct answer to this question ought to be to use Test::Output:

#!/usr/bin/perl

use strict; use warnings;

use Test::More tests => 1;
use Test::Output;

sub myfunc { print "This is a test\n" }

stdout_is(\&myfunc, "This is a test\n", 'myfunc() returns test output');

Output:

C:\Temp> tm
1..1
ok 1 - myfunc() returns test output

I am leaving the original answer for reference as, I believe, it still illustrates a useful technique.

You can localize `STDOUT` and reopen to a scalar before calling the function, restore afterward:

#!/usr/bin/perl

use strict; use warnings;

use Test::More tests => 1;

sub myfunc { print "This is a test\n" }

sub invoke {
    my $sub = shift;
    my $stdout;
    {
        local *STDOUT;
        open STDOUT, '>', \$stdout
            or die "Cannot open STDOUT to a scalar: $!";
        $sub->(@_);
        close STDOUT
            or die "Cannot close redirected STDOUT: $!";
    }
    return $stdout;
}

chomp(my $ret =  invoke(\&myfunc));

ok($ret eq "This is a test", "myfunc() prints test string" );
diag("myfunc() printed '$ret'");

Output:

C:\Temp> tm
1..1
ok 1 - myfunc() prints test string
# myfunc() printed 'This is a test'

For versions of `perl` older than 5.8, you probably need to use IO::Scalar, but I do not know much about how things worked before 5.8.

Problem

I'm trying to use Test::More to unit test Perl functions that print to the screen. I understand that this output may interfere with tools such as prove. How can I capture this output so I can print it with `diag()`, and also run tests on the output itself?

Original source