Подтвердить что ты не робот

Подавление вывода на консоль с рубином

Я пишу некоторые модульные тесты, например:

def executing_a_signal
  a_method(a_signal.new, a_model, a_helper);
  assert_equal(new_state, a_model.state)
end

Тесты работают нормально, но метод, который выполняется непосредственно перед утверждением для выполнения логики, печатает различные сообщения на консоли, главным образом через puts.

Есть ли быстрый, возможно встроенный способ подавления вывода на консоль? Меня интересует только конечный эффект метода на объекте модели, и для того, чтобы сохранить чистоту консоли в целом, я надеялся найти способ просто предотвратить весь вывод на консоль без повторной записи или комментирования тех puts только для моих тестов.

Это определенно не критическая проблема, но очень хотелось бы услышать любые мысли или идеи (или обходные пути) на ней.

4b9b3361

Ответ 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