2012-06-12 58 views
42

我想測試Rails記錄器在我的一些規格中接收消息。我正在使用Logging gemRSpec:如何測試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") 

使用的RSpec的新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 
+0

我有類似的情況下希望我的預期字符串部分字符串,我無法弄清楚迄今爲止,如何處理它,任何幫助? –

+3

@AmolPujari '希望(Rails.logger)。爲了接收(:信息)。(/ partial_string /)' 其中「partial_string」是您正在查找的字符串。簡單的正則表達式比較 – absessive

+0

這太棒了,我檢查我沒有得到*任何東西*記錄到錯誤,並檢查對Rspec的任何匹配,這很好: 'expect(Rails.logger).to_not receive(:error ).with(任何東西)' –

11

使用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 

注意上面的變化設置.ordered對其他期望是重要的等開始失敗。

在Rails方面,我已經驗證了上面的代碼與infodebug水平似乎並沒有直截了當地工作,但是預期工作。我想在內部使用的調試和信息水平,這可能會導致像

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

引用錯誤,因爲Rails的它:

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

相關問題