Using Test::Unit to assert messages appeared on $stdout/$stderr

I’m sorta new to the whole unit testing thing, and my only prior exposure to
software testing is using DejaGnu/Expect. I’m just starting to use
Test::Unit, and I have a case where a certain action should produce a
warning message. There is no other side effect that can be tested to make
sure the condition that produced the warning was indeed exercised properly,
just the appearance of the message.

I don’t see any obvious assertion in Test::Unit that allows checking of I/O
to $stdout/$stderr. What’s a good way to test for such a case?

Thanks,

  • jeff

Get stringio.rb from www.allruby.com/rpkg/stringio.rb

require ‘stringio.rb’

class TestSomething < Test::Unit::TestCase
def set_up
$stdin, @out, @err = StringIO.new, StringIO.new, StringIO.new
end

def test_something
redir(@out) { say_hello }

assert_equal "hello world\n", @out.to_s

end

you can test input as well

def test_input
$stdin.puts “user_command”
$stdin.rewind
redir(@out) { command_that_acts_based_on_stdin }

assert_equal "was issued: <user_command>\n", @out.to_s    

end
end

Massimiliano

···

On Sat, Aug 17, 2002 at 04:41:43AM +0900, Gray, Jeff wrote:

I’m sorta new to the whole unit testing thing, and my only prior exposure to
software testing is using DejaGnu/Expect. I’m just starting to use
Test::Unit, and I have a case where a certain action should produce a
warning message. There is no other side effect that can be tested to make
sure the condition that produced the warning was indeed exercised properly,
just the appearance of the message.

I don’t see any obvious assertion in Test::Unit that allows checking of I/O
to $stdout/$stderr. What’s a good way to test for such a case?

If something is hard to test, that’s a good indication that the design
needs to change.

In your case, your object is writing to stdout or stderr. Because it is
accessing the output streams through a global variable, you cannot
intercept those calls and test that they are correct.

A unit test is meant to test a unit (a class, for example) in
isolation. That means you have to be able to isolate the unit from its
environment, which means it should not read or write global variables.
In fact it should only make calls to objects that it has as instance
variables or are passed to it’s methods as parameters.

I would therefore change my design so that the object under test is
given a stream object to which it writes messages. In my unit tests, I
would pass in a mock stream object that asserts that it is called
correctly. In my application and end-to-end functional tests, I would
pass in a reference to stdout or stderr so that output actually gets
written.

I have written a Test::Mock package that you might help you mock the
output stream object. You can get it from here:

http://www.ruby-lang.org/~knu/cgi-bin/cvsweb.cgi/rough/lib/testmock/

Cheers,
Nat.

···

On Fri, 2002-08-16 at 20:41, Gray, Jeff wrote:

I’m sorta new to the whole unit testing thing, and my only prior exposure to
software testing is using DejaGnu/Expect. I’m just starting to use
Test::Unit, and I have a case where a certain action should produce a
warning message. There is no other side effect that can be tested to make
sure the condition that produced the warning was indeed exercised properly,
just the appearance of the message.

I don’t see any obvious assertion in Test::Unit that allows checking of I/O
to $stdout/$stderr. What’s a good way to test for such a case?


Dr. Nathaniel Pryce, Technical Director, B13media Ltd.
Studio 3a, 22-24 Highbury Grove, London N5 2EA, UK
http://www.b13media.com