Подавление вывода на консоль с рубином
Я пишу некоторые модульные тесты, например:
def executing_a_signal
a_method(a_signal.new, a_model, a_helper);
assert_equal(new_state, a_model.state)
end
Тесты работают нормально, но метод, который выполняется непосредственно перед утверждением для выполнения логики, печатает различные сообщения на консоли, главным образом через puts
.
Есть ли быстрый, возможно встроенный способ подавления вывода на консоль? Меня интересует только конечный эффект метода на объекте модели, и для того, чтобы сохранить чистоту консоли в целом, я надеялся найти способ просто предотвратить весь вывод на консоль без повторной записи или комментирования тех puts
только для моих тестов.
Это определенно не критическая проблема, но очень хотелось бы услышать любые мысли или идеи (или обходные пути) на ней.
Ответы
Ответ 1
Я использую следующий фрагмент в тестах для захвата и тестирования STDOUT
def capture_stdout(&block)
original_stdout = $stdout
$stdout = fake = StringIO.new
begin
yield
ensure
$stdout = original_stdout
end
fake.string
end
С помощью этого метода выше будет:
def executing_a_signal
capture_stdout { a_method(a_signal.new, a_model, a_helper) }
assert_equal(new_state, a_model.state)
end
Ответ 2
Существует два решения: перенаправление туда, где puts
записывается (решение, данное @cldwalker выше), или переписывание самого метода puts
как no-op. (Реализация должна быть очевидной: 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
Ответ 3
Чуть более чистое решение @cldwalker:
def silenced
$stdout = StringIO.new
yield
ensure
$stdout = STDOUT
end
silenced do
something_that_prints
end
Ответ 4
reopen '/dev/null'
Другим вариантом является перенаправление на /dev/null
с помощью:
STDOUT.reopen('/dev/null', 'w')
STDERR.reopen('/dev/null', 'w')
Этот метод используется WEBrick::Daemon
для stdlib (источник переключения).
Преимущество заключается в том, что он более эффективен, чем StringIO.new
, поскольку он не сохраняет stdout на StringIO
, но он менее портативен.
Ответ 5
Я просто использовал приведенный ниже код в начале моего .rb файла.. поэтому он отключил все консольные заявления печати.
def puts(*args) end