Railsロガーが私の仕様の一部でメッセージを受信することをテストしようとしています。 Logging gem を使用しています。
次のようなクラスがあるとしましょう:
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にコピーすると成功する理由すらわかりません。)
ロギングをテストするための良い解決策は何ですか?
通常、ロガーをテストしたくないと思いますが、役に立つ場合があります。
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
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
ロギング機能をテストすることが目的の場合、標準ストリームへの出力の検証も検討できます。
これにより、モックプロセスが不要になり、メッセージが実際に想定される場所(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
。