如何对打印到屏幕的Perl函数进行单元测试?

时间:2009-10-08 14:40:26

标签: perl unit-testing testing

我正在尝试使用Test::More对打印到屏幕的Perl函数进行单元测试。

我了解此输出可能会干扰prove等工具。

如何捕获此输出以便我可以使用diag()进行打印,还可以对输出本身进行测试?

3 个答案:

答案 0 :(得分:32)

更新:恕我直言,这个问题的正确答案应该是使用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');

输出:

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

我将原始答案留待参考,我相信,它仍然说明了一种有用的技术

您可以在调用函数之前本地化STDOUT并重新打开标量,然后恢复:

#!/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'");

输出:

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

对于早于5.8的perl版本,您可能需要使用IO::Scalar,但我对5.8之前的工作原理不太了解。

答案 1 :(得分:7)

我会考虑让模块为您处理这个问题。请看Capture::Tiny

答案 2 :(得分:6)

如果这是您自己编写的代码,请更改它以使print语句不使用默认文件句柄。相反,给自己一个方法将输出文件句柄设置为你喜欢的任何东西:

sub my_print {
     my $self = shift;
     my $fh = $self->_get_output_fh;
     print { $fh } @_;
     }

sub _get_output_fh { $_[0]->{_output}  || \*STDOUT }
sub _set_output_fh { $_[0]->{_output} = $_[1] } # add validation yourself

测试时,您可以调用_set_output_fh为其提供测试文件句柄(甚至可能是IO::Null句柄)。当另一个人想要使用你的代码但是捕获输出时,他们不必向后弯腰来执行它,因为他们可以提供自己的文件句柄。

当您发现代码的一部分难以测试或者您必须跳过箍来使用时,您的设计可能很糟糕。我仍然对测试代码如何使这些事情变得明显感到惊讶,因为我经常不会考虑它们。如果难以测试,请轻松测试。如果你这样做,你通常会赢。