Redirecting stdout to a String

I wrote a unit test for a method that writes to stdout. I got it to
work, but I’m not sure my approach was the best. Is there a more
recommended way to do this?

# Create a String with a singleton write method
# that allows it to take the place of stdout.
actual = '''
def actual.write(data); self << data; end
old_stdout, $stdout = $stdout, actual

# Invoke method to test that writes to stdout.

$stdout = old_stdout # restore stdout

On Mar 24, 2006, at 7:50 AM, Mark V. wrote:

I wrote a unit test for a method that writes to stdout. I got it to
work, but I’m not sure my approach was the best. Is there a more
recommended way to do this?

# Create a String with a singleton write method
# that allows it to take the place of stdout.
actual = '''
def actual.write(data); self << data; end

See the standard library, StringIO.

James Edward G. II

On 3/24/06, James Edward G. II [email protected] wrote:

See the standard library, StringIO.

Thanks! I have that working, but it’s not clear to me how that is
better. In fact, it seems to take more code to do it that way. I now
have this.

require ‘stringio’

sio = StringIO.new
old_stdout, $stdout = $stdout, sio

Invoke method to test that writes to stdout.

$stdout = old_stdout # restore stdout
actual = sio.string

Am I overcomplicating this?

On 3/24/06, Mark V. [email protected] wrote:

actual = '''

sio = StringIO.new

I made a benchmark of both solutions, but I was surprised how small the
difference was.
StringIO is faster, as I expected. See below for results.
Apart from that I feel that stringio will have adavantages for giving
you a
complete API.
But for a quick solution the singleton method seems elegant and I like
it a
lot, after all
you do not require( which was not benchmarked!!!) ‘stringio’ and run a
core
script.

But for reusable code I’ll defenitely go for stringio.

Cheers
Robert

---------------------------------- 8<

cat stringio.rb;ruby stringio.rb
#!/usr/bin/env ruby

Create a String with a singleton write method

that allows it to take the place of stdout.

require ‘benchmark’
require ‘stringio’

n = 500000
r1 = r2 = nil
Benchmark.bm do
|bm|
bm.report “single string” do
actual = ‘’

        def actual.write(data); self << data.to_s; end
        old_stdout, $stdout = $stdout, actual
        n.times{ |i| $stdout.write i }
        $stdout = old_stdout
        r1= actual
    end # do

    bm.report "stringio     " do
             sio = StringIO.new
             old_stdout, $stdout = $stdout, sio
             n.times{ |i| $stdout.write i}
             # Invoke method to test that writes to stdout.
             $stdout = old_stdout # restore stdout
             r2 = sio.string
    end # do

end # do

raise Exception unless r1 == r2

  user     system      total        real

single string 1.460000 0.000000 1.460000 ( 1.823057)
stringio 1.320000 0.010000 1.330000 ( 1.828039)


Deux choses sont infinies : l’univers et la bêtise humaine ; en ce qui
concerne l’univers, je n’en ai pas acquis la certitude absolue.

  • Albert Einstein

On Mar 24, 2006, at 8:12 AM, Mark V. wrote:

def actual.write(data); self << data; end

See the standard library, StringIO.

Thanks! I have that working, but it’s not clear to me how that is
better. In fact, it seems to take more code to do it that way.

You originally asked:

Is there a more recommended way to do this?

I was just trying to answer that question.

I prefer the StringIO method because it is a full mock API, as Robert
Dober said. However, if you know you will only need the one method
(now and forever?!) there is probably little difference.

James Edward G. II