有两种解决方案:重定向puts
写入位置(上面@cldwalker 给出的解决方案),或者将puts
方法本身覆盖为无操作。(实现应该很明显:)module Kernel; def puts(*args) end end
。
但是,在这种情况下,真正最好的解决方案是“听你的测试”。因为,通常当某些东西难以测试时,您的测试实际上是在试图告诉您您的设计有问题。在这种情况下,我闻到了对单一责任原则的违反:为什么模型对象需要知道如何写入控制台?它的职责是代表一个领域概念,而不是记录!这就是 Logger 对象的用途!
因此,另一种解决方案是让 Model 对象将日志记录的责任委托给 Logger 对象,并使用依赖注入向 Model 对象注入合适的 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
在您的模型中说,或者您担心有人可能会忘记调用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