2009-10-08 70 views

回答

32

UPDATE:恕我直言,正確回答這個問題應該是使用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' 

對於perl版本比5.8老,你可能需要使用IO::Scalar,但我對5.8之前的工作情況瞭解不多。

+4

正當我想我在這麼浪費太多時間時,我學到了一些很酷的東西!謝謝。 – FMc 2009-10-08 15:50:30

+0

呃,從技術上說他們不這樣做,但是他們確實很好。 :p(+1,這是一個很棒的模塊) – 2009-10-08 19:08:04

+0

我沒有降低它的效果,但我認爲原始答案做了太多的工作來完成工作。它太複雜了。儘管我對Test :: Output有點偏向,但我將它作爲最後的手段。 – 2009-10-08 23:13:09

7

我會考慮讓模塊爲你處理這個問題。看看Capture::Tiny

+0

+1我不知道Capture :: Tiny。 – 2009-10-08 16:52:38

5

如果這是您正在編寫的代碼,請對其進行更改以使打印語句不使用默認文件句柄。相反,給自己一個方法來設置輸出文件句柄到你喜歡的東西:

 
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手柄)。當另一個人想要使用你的代碼但是捕獲輸出時,他們不必向後彎曲,因爲他們可以提供自己的文件句柄。

當您發現難以測試的部分代碼或者您必須跳過這些環節才能使用時,您的設計可能很糟糕。我仍然對測試代碼如何使這些事情變得明顯感到驚訝,因爲我經常不會考慮它們。如果很難測試,可以很容易測試。如果你這樣做,你一般會贏。

+0

阿門。編寫單元測試(或者如果您使用TDD進行計劃,無論您是否知道)通常都會導致設計上的顯着改進。 – DVK 2009-10-09 03:37:14