有两种解决方案:重定向
puts
输出的位置(@cldwalker提出的解决方案),或者覆盖
puts
方法本身,使其成为无操作。(具体实现很明显:
module Kernel; def puts(*args) end end
)。
然而,在这种情况下,“倾听你的测试”才是最好的解决方案。因为,通常当某些东西难以测试时,你的测试真正想告诉你的是你的设计有问题。在这种情况下,我嗅到了单一职责原则的违反:为什么一个模型对象需要知道如何写入控制台?它的职责是代表一个领域概念,而不是日志记录!这就是Logger对象的作用!
因此,另一个解决方案是让模型对象将日志记录的责任委托给Logger对象,并使用依赖注入为模型对象注入合适的Logger对象。这样,你可以简单地为测试注入一个虚拟的日志记录器。以下是一个示例:
#!/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
至少,Model对象应该将其记录“到”的IO
对象作为参数传入,这样您就可以在测试中简单地注入StringIO.new
:
#!/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
如果你仍然希望在你的模型中只需说puts whatever
或者你担心有人可能会忘记调用logger对象上的puts
方法,那么你可以提供自己的(私有的)puts方法:
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
STDOUT
常量,那么您将不得不单独重新定义它。 - Alexander Bird