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

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

Я пытаюсь проверить, что регистратор 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 приведет к его прохождению.)

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

4b9b3361

Ответ 1

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

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

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

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

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

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

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

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

Ответ 2

С RSpec 3+ версия

Фактический код, содержащий один вызов 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

Фактический код, содержащий несколько вызовов 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

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

  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

Примечание в приведенной выше настройке варианта .ordered важно, иначе набор ожиданий начинает сбой.

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

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

Ответ 3

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

Это избавит вас от насмешливого процесса и проверит, действительно ли сообщения будут в конечном итоге там, где они должны были (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.

Ответ 4

Вместо использования этой строки перед тем, как сообщение будет зарегистрировано:

expect(Rails.logger).to receive(:info).with("some message")
something that triggers the logger...

Вы можете установить регистратор Rails как шпиона и использовать вместо него have_received:

allow(Rails.logger).to receive(:info).at_least(:once)

something that triggers the logger...

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