Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation

Tests for 'printing' code

by dda (Friar)
on Oct 19, 2003 at 14:50 UTC ( #300382=perlquestion: print w/replies, xml ) Need Help??
dda has asked for the wisdom of the Perl Monks concerning the following question:

Hi All

I want to create tests for a code which contains 'print' statements. For example:

#!/usr/bin/perl -w use strict; use Test::More qw(no_plan); ok(test_foo() eq "bar"); sub test_foo { print "foo"; return "bar"; } __OUTPUT__ foook 1 1..1
Is there a way to eliminate 'foo' printing in the tests output, and, more important, to analyze information printed by the tested subroutine?


Replies are listed 'Best First'.
Re: Tests for 'printing' code
by jeffa (Bishop) on Oct 19, 2003 at 15:01 UTC
    Personally, i think this is as expected and you should change you code to not print from inside your subroutine. Your subroutine should collect data and store it in a scalar and return that instead of just printing. This is a common mistake people make, thinking that subroutines should print directly to STDOUT - sometimes they should, but most of the time you are better off capturing the output and returning it. This helps lead to more reusable code. Besides, are you more interested in testing the output, or the message string?
    use Test::More qw(no_plan); ok(test_foo() eq "bar"); sub print_foo_message { print "foo"; } sub test_foo { return "bar"; }


    (the triplet paradiddle with high-hat)
      Thanks, jeffa. I expected that printing from subroutines will be blamed here. :) But how you will test sub print_foo_message then? Of course, it's too simple, but it is just an example. I'm interested in testing the output, at least, I need to test the fact that something was printed.


        You don't. :) It's just a canned message. Why do you need to test if something was printed? That's not very helpful. Instead test what is about to be printed, before you print it. In fact, you don't need to print anything at all from your test suite (except user interaction messages like "Test this?" and your own debugging when your test has "bugs").

        Now, don't get me wrong - what you originally wanted can be done, but do you really want to go there?

        use IO::Scalar; use Test::More qw(no_plan); my $string; tie *STDOUT, 'IO::Scalar', \$string; ok(test_foo() eq 'bar'); ok($string eq 'foo'); $string = ''; ok(test_bar() eq 'baz'); ok($string eq 'bar'); $string = ''; sub test_foo { print 'foo'; return 'bar'; } sub test_bar { print 'bar'; return 'baz'; }
        No thanks. :)


        (the triplet paradiddle with high-hat)
Re: Tests for 'printing' code
by adrianh (Chancellor) on Oct 19, 2003 at 15:34 UTC

    I've had this hanging around for ages, but I'm not happy with it yet. I should really generalise it into some sort of Test::Filehandle. However it may be of use as a starting point:

    #! /usr/bin/perl -w package Test::Output; use 5.005; use strict; use Sub::Uplevel; use Test::Builder; use IO::File; use Fcntl; use Symbol qw(qualify_to_ref gensym); use base qw(Exporter); use vars qw($VERSION @EXPORT); $VERSION = '0.01'; @EXPORT = qw(output_is output_isnt output_like output_unlike); sub _try_as_caller { my ($sub, $level) = @_; eval { uplevel $level, $sub }; return $@; }; my $Last_output; sub _get_output { my ($code, $fh, $level) = @_; $fh = qualify_to_ref($fh, caller); my $tmp = IO::File->new_tmpfile or die "no tmp file ($!)"; my $old = gensym; *$old = *$fh; local *$fh = $tmp; my $exception = _try_as_caller($code, $level); *$fh = *$old; die if $exception; seek $tmp, SEEK_SET, 0 or die "could not seek ($!)"; my $output = ''; my $n; while ($n = read $tmp, $output, 1024, length($output)) {}; die "could not read ($!)" unless defined($n); return($Last_output = $output); }; sub _test_output { my ($method, $code, $expected, $fh, $name) = @_; my $builder = Test::Builder->new; my $todo = $builder->exported_to; local $Test::Builder::Level = 2; $builder->$method(_get_output($code, $fh, 6), $expected, $name); }; sub output_is (&$*;$) { _test_output('is_eq', @_) }; sub output_isnt (&$*;$) { _test_output('isnt_eq',@_) }; sub output_like (&$*;$) { _test_output('like', @_) }; sub output_unlike (&$*;$) { _test_output('unlike', @_) }; sub last { $Last_output }; 1;

    Use something like:

    output_is { hello() } "hello world\n", STDOUT, "hello world"; output_isnt { hello() } "goodbye", STDOUT, "not goodbye"; output_unlike { hello() } qr/bye/, STDOUT, "didn't print bye +"; output_like { hello() } qr/hello/, STDOUT, "printed hello"; like(Test::Output->last, qr/world/, "... and world");

    (yes, I know,  _get_output sucks ;-)

Re: Tests for 'printing' code
by PodMaster (Abbot) on Oct 19, 2003 at 15:10 UTC
    I don't see why not ... look for output methods in Test::Builder (if you're wondering why I say Test::Builder, then you haven't looked inside Test::More), or simply open STDOUT to something else.

    MJD says "you can't just make shit up and expect the computer to know what you mean, retardo!"
    I run a Win32 PPM repository for perl 5.6.x and 5.8.x -- I take requests (README).
    ** The third rule of perl club is a statement of fact: pod is sexy.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://300382]
Approved by BazB
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others chilling in the Monastery: (4)
As of 2018-05-27 18:27 GMT
Find Nodes?
    Voting Booth?