2012-06-12 3 views
42

Я пытаюсь проверить, что регистратор Rails получает сообщения в некоторых моих спецификациях. Я использую Logging gem.RSpec: как тестировать ожидания сообщений журнала 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 пропустит его.)

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

+1

Этот вопрос возникал несколько раз на SO, см. Например [здесь] (http://stackoverflow.com/questions/153234/how-deep-are-your-unit-tests) и [здесь] (http: //stackoverflow.com/questions/1168151/unit-testing-logging-and-dependency-injection), и общий консенсус заключался в том, что вы не проверяете ведение журнала, если это не требование к проекту. –

+1

Искусство, спасибо за комментарий. Я читал их. Это может быть окончательный ответ. – keruilin

ответ

83

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

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

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

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

более новый expect синтаксис RSpec в:

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 
+0

У меня есть аналогичный случай, ожидая, что моя ожидаемая строка - это частичная строка, я пока не могу понять, как с этим справиться, любая помощь? –

+3

@AmolPujari 'ожидать (Rails.logger). Получать (: info).с (/ partial_string /) ' где« partial_string »- это строка, которую вы ищете. Simple Regex compare – absessive

+0

Это замечательно, я проверяю, что я вообще ничего не получаю *, зарегистрирован на ошибку, и проверка на что-либо совпадение Rspec делает это красиво: 'ожидать (Rails.logger) .to_not receive (: error) .with (ничего) ' –

11

С Rspec 3+ версии

Фактический код, содержащий один вызов Rails.logger.error:

Rails.logger.error "Some useful error message" 

Spec код:

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" 

Spec Код:

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

Примечание в приведенной выше настройке изменения .ordered важны для других ожиданий s et start failing.

В контексте Rails Я проверил код выше, чтобы работать, как ожидается, однако с info и debug уровнями это, кажется, не работает в прямой манере. Я предполагаю, что это из-за Rails внутри с помощью отладки и информация уровней, которые могут вызывать ошибки как

(#<ActiveSupport::Logger:0x00000006c55778>).info(*(any args)) 
    expected: 1 time with any arguments 
    received: 4 times with any arguments 

Ссылки:

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

1

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

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

С RSpec-х output matcher (введено в версии 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.

Смежные вопросы