I am writing some unit tests like the following:
def executing_a_signal
a_method(a_signal.new, a_model, a_helper);
assert_equal(new_state, a_model.state)
end
The tests work fine, but the method which runs just before the assertion to execute the logic prints various messages to the console, mainly via puts
.
Is there a quick, perhaps built-in, way to suppress that output to the console? I am only interested in the final effect of the method on the model object, and for the sake of keeping the console clean basically, I was hoping to find a way to simply prevent all output to the console without re-writing or commenting out those puts
statements just for my tests.
It is definitely not a critical issue, but would very much like to hear any thoughts or ideas (or workaround) on it.
I use the following snippet in tests to capture and test STDOUT
def capture_stdout(&block)
original_stdout = $stdout
$stdout = fake = StringIO.new
begin
yield
ensure
$stdout = original_stdout
end
fake.string
end
With this method, the above would become:
def executing_a_signal
capture_stdout { a_method(a_signal.new, a_model, a_helper) }
assert_equal(new_state, a_model.state)
end
A slightly cleaner take on @cldwalker's solution:
def silenced
$stdout = StringIO.new
yield
ensure
$stdout = STDOUT
end
silenced do
something_that_prints
end
There's two solutions: redirecting where puts
writes to (the solution given by @cldwalker above), or overwriting the puts
method itself to be a no-op. (The implementation should be obvious: module Kernel; def puts(*args) end end
).
However, in this case, what would really be the best solution is "listening to your tests". Because, oftentimes when something is awkward to test, your tests are really trying to tell you that something is wrong with your design. In this case, I smell a violation of the Single Responsibility Principle: why the heck does a Model object need to know how to write to the console? Its responsibility is representing a Domain Concept, not logging! That's what Logger objects are for!
So, an alternative solution would be to have the Model object delegate the responsibility for logging to a Logger object, and use dependency injection to inject the Model object with a suitable Logger object. That way, you can simply inject a fake logger for the test. Here's an example:
#!/usr/bin/env ruby
class SomeModel
def initialize(logger=Kernel) @logger = logger end
def some_method_that_logs; @logger.puts 'bla' end
end
require 'test/unit'
require 'stringio'
class TestQuietLogging < Test::Unit::TestCase
def setup; @old_stdout, $> = $>, (@fake_logdest = StringIO.new) end
def teardown; $> = @old_stdout end
def test_that_default_logging_is_still_noisy
SomeModel.new.some_method_that_logs
assert_equal "bla\n", @fake_logdest.string
end
def test_that_logging_can_be_made_quiet
fake_logger = Object.new
def fake_logger.puts *args; end
SomeModel.new(fake_logger).some_method_that_logs
assert_equal '', @fake_logdest.string
end
end
At the very least, the Model object should take the IO
object that it is logging to as an argument, so that you can simply inject StringIO.new
into it for the test:
#!/usr/bin/env ruby
class SomeModel
def initialize(logdest=$>) @logdest = logdest end
def some_method_that_logs; @logdest.puts 'bla' end
end
require 'test/unit'
require 'stringio'
class TestQuietLogging < Test::Unit::TestCase
def setup; @old_stdout, $> = $>, (@fake_logdest = StringIO.new) end
def teardown; $> = @old_stdout end
def test_that_default_logging_is_still_noisy
SomeModel.new.some_method_that_logs
assert_equal "bla\n", @fake_logdest.string
end
def test_that_logging_can_be_made_quiet
fake_logdest = (@fake_logdest = StringIO.new)
SomeModel.new(fake_logdest).some_method_that_logs
assert_equal '', @fake_logdest.string
assert_equal "bla\n", fake_logdest.string
end
end
If you still want to be able to just say puts whatever
in your Model or you are afraid that someone might forget to call puts
on the logger object, you can provide your own (private) puts method:
class SomeModel
def initialize(logdest=$>) @logdest = logdest end
def some_method_that_logs; puts 'bla' end
private
def puts(*args) @logdest.puts *args end
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With