RSpec: как проверить ожидания сообщений от Rails logger?

Я пытаюсь проверить, что регистратор Rails получает сообщения в некоторых моих спецификациях. Я используюЖемчужина лесозаготовок.

Допустим, у меня есть такой класс:

class BaseWorker

  def execute
    logger.info 'Starting the worker...'
  end

end

И спецификации вроде:

describe BaseWorker do

  it 'should log an info message' do
    base_worker = BaseWorker.new
    logger_mock = double('Logging::Rails').as_null_object
    Logging::Rails.stub_chain(:logger, :info).and_return(logger_mock)

    logger_mock.should_receive(:info).with('Starting the worker...')
    base_worker.execute
    Logging::Rails.unstub(:logger)
  end

end

Я получаю следующее сообщение об ошибке:

 Failure/Error: logger_mock.should_receive(:info).with('Starting worker...')
   (Double "Logging::Rails").info("Starting worker...")
       expected: 1 time
       received: 0 times

Я опробовал несколько различных подходов, чтобы пройти спецификацию. Это работает, например:

class BaseWorker

  attr_accessor :log

  def initialize
    @log = logger
  end

  def execute
    @log.info 'Starting the worker...'
  end

end

describe BaseWorker do
  it 'should log an info message' do
    base_worker = BaseWorker.new
    logger_mock = double('logger')
    base_worker.log = logger_mock

    logger_mock.should_receive(:info).with('Starting the worker...')
    base_worker.execute
  end
end

Но необходимость настраивать доступную переменную экземпляра, подобную этой, кажется, что хвост здесь виляет собакой. (На самом деле, я даже не уверен, почему копирование логгера в @log сделает его успешным.)

Какое хорошее решение для тестирования регистрации?

 keruilin12 июн. 2012 г., 21:10
Арт, спасибо за комментарий. Я читал те. Это может быть окончательным ответом.
 Art Shayderov12 июн. 2012 г., 19:49
Этот вопрос возник несколько раз на SO, см. Напримерhere а такжеhere и общее согласие заключалось в том, что вы не тестируете протоколирование, если это не является требованием проекта.

Ответы на вопрос(3)

С версией RSpec 3+

Actual code containing single invocation of Rails.logger.error:

Rails.logger.error "Some useful error message"

Спец код:

expect(Rails.logger).to receive(:error).with(/error message/)

Если вы хотите, чтобы сообщение об ошибке действительно регистрировалось во время выполнения спецификации, используйте следующий код:

expect(Rails.logger).to receive(:error).with(/error message/).and_call_original

Actual code containing multiple invocations of Rails.logger.error:

Rails.logger.error "Technical Error Message"
Rails.logger.error "User-friendly Error Message"

Спец код:

expect(Rails.logger).to receive(:error).ordered
expect(Rails.logger).to receive(:error).with(/User-friendly Error /).ordered.and_call_original

Also if you care about just matching the first message and not any subsequent messages then you can use following

  expect(Rails.logger).to receive(:debug).with("Technical Error Message").ordered.and_call_original
  expect(Rails.logger).to receive(:debug).at_least(:once).with(instance_of(String)).ordered

Note in above variation setting .ordered is important else expectations set start failing.

Рекомендации:

http://www.relishapp.com/rspec/rspec-mocks/v/3-4/docs/setting-constraints/matching-arguments

http://www.relishapp.com/rspec/rspec-mocks/v/3-4/docs/setting-constraints/message-order

Если ваша цель состоит в том, чтобы протестировать функциональность регистрации, вы можете также рассмотреть возможность проверки вывода в стандартные потоки.

Это избавит вас от процесса насмешки и проверки того, действительно ли сообщения окажутся там, где они должны (STDOUT / STDERR).

С RSpecвыходной сопоставитель (введено в 3.0) вы можете сделать следующее:

expect { my_method }.to output("my message").to_stdout
expect { my_method }.to output("my error").to_stderr

В случае библиотек, таких какLogger или жеLogging вам, возможно, придется использоватьoutput.to_<>_from_any_process.

Решение Вопроса

Хотя я согласен, что вы, как правило, не хотите тестировать логгеры, иногда это может быть полезно.

Я имел успех с ожиданиями наRails.logger.

Использование RSpec устарелоshould синтаксис:

Rails.logger.should_receive(:info).with("some message")

Использование RSpec'а новееexpect синтаксис:

expect(Rails.logger).to receive(:info).with("some message")

Note: В спецификации контроллера и модели вы должны поставить эту строкуbefore сообщение зарегистрировано Если вы поместите его после, вы получите сообщение об ошибке, подобное этому:

Failure/Error: expect(Rails.logger).to receive(:info).with("some message")
       (#<ActiveSupport::Logger:0x007f27f72136c8>).info("some message")
           expected: 1 time with arguments: ("some message")
           received: 0 times
 05 мар. 2015 г., 17:09
что именно вы подразумеваете под "quot; вы должны поставить эту строку, прежде чем сообщение будет зарегистрировано"? Ожидание появляется в коде перед кодом, который генерирует журнал? Я делаю это и получаю сообщение об ошибке, потому что регистратор получает сообщение, которое регистрируется из материалов, которые были сделаны в моемlet выражения передit блок даже работает
 26 янв. 2015 г., 21:28
@AmolPujariexpect(Rails.logger).to receive(:info).with(/partial_string/)  где «частичная_строка» это строка, которую вы ищете. Простое сравнение регулярных выражений
 09 дек. 2014 г., 13:45
У меня есть аналогичный случай, ожидаю, что моя ожидаемая строка является частичной строкой, я до сих пор не мог понять, как с этим справиться, любая помощь?
 13 февр. 2015 г., 10:17
Это здорово, я проверяю, что я не получаюanything at all вошел в систему с ошибкой и проверкой на соответствие Rspec что угодно:expect(Rails.logger).to_not receive(:error).with(anything)
 17 нояб. 2015 г., 21:40
@ sixty4bit это означает, что expext .. receive работает как прослушиватель событий - сначала нужно настроить его, а затем запустить код, который будет регистрировать сообщение, которое вы хотите перехватить

Ваш ответ на вопрос