76

Railsロガーが私の仕様のいくつかでメッセージを受信することをテストしようとしています。私はLogginggemを使用しています。

私がこのようなクラスを持っているとしましょう:

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

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

4

6 に答える 6

132

私はあなたが一般的にロガーをテストしたくないことに同意しますが、それが役に立つかもしれない時があります。

私はに期待して成功しました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
于 2012-07-11T05:41:10.537 に答える
23

RSpec3+バージョンを使用

の単一の呼び出しを含む実際のコード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

于 2015-12-15T14:36:46.293 に答える
14

メッセージがログに記録される前にこの行を使用する代わりに:

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
于 2019-09-19T14:48:57.730 に答える
9

ロギング機能をテストすることが目標である場合は、標準ストリームへの出力を検証することも検討してください。

これにより、モックプロセスが不要になり、メッセージが実際に想定どおりの場所に到達するかどうかをテストできます(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

于 2017-06-03T18:29:49.243 に答える
5

テストの一貫性を維持したいが、最後に期待値を設定する場合は、セットアップに追加する必要があります。

setup do
 allow(Rails.logger).to receive(:info)
end
...

it 'should log an info message' do
 {code}

  expect(Rails.logger).to have_received(:info).with('Starting the worker...')
end

于 2020-03-20T11:36:14.830 に答える
1

私でさえ非常によく似たエラーがありました:

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

以下は私のために働いた、

expect { my_method }.
            to output(/error messsage/).to_stdout_from_any_process

参照:https ://relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/output-matcher

于 2020-03-19T06:12:35.827 に答える