web-dev-qa-db-ja.com

RSpec:テスト方法Railsロガーメッセージの期待?

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にコピーすると成功する理由すらわかりません。)

ロギングをテストするための良い解決策は何ですか?

50
keruilin

通常、ロガーをテストしたくないと思いますが、役に立つ場合があります。

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
106
Tanzeeb Khalili

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

17
Jignesh Gohel

ロギング機能をテストすることが目的の場合、標準ストリームへの出力の検証も検討できます。

これにより、モックプロセスが不要になり、メッセージが実際に想定される場所(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

LoggerLoggingなどのライブラリの場合は、output.to_<>_from_any_process

5
thisismydesign