web-dev-qa-db-fra.com

RSpec: comment tester Rails attentes du message de l'enregistreur?

J'essaie de tester que l'enregistreur Rails reçoit des messages dans certaines de mes spécifications. J'utilise Gemme de journalisation .

Disons que j'ai une classe comme celle-ci:

class BaseWorker

  def execute
    logger.info 'Starting the worker...'
  end

end

Et une spécification comme:

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

J'obtiens le message d'échec suivant:

 Failure/Error: logger_mock.should_receive(:info).with('Starting worker...')
   (Double "Logging::Rails").info("Starting worker...")
       expected: 1 time
       received: 0 times

J'ai essayé plusieurs approches différentes pour faire passer la spécification. Cela fonctionne par exemple:

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

Mais avoir à configurer une variable d'instance accessible comme celle-ci semble que la queue remue le chien ici. (En fait, je ne sais même pas pourquoi la copie de l'enregistreur dans @log le ferait passer.)

Quelle est une bonne solution pour tester la journalisation?

50
keruilin

Bien que je convienne que vous ne voulez généralement pas tester les enregistreurs, il peut parfois être utile.

J'ai eu du succès avec des attentes sur Rails.logger.

Utilisation de la syntaxe obsolète should de RSpec:

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

Utilisation de la nouvelle syntaxe expect de RSpec:

expect(Rails.logger).to receive(:info).with("some message")

Remarque: Dans les spécifications du contrôleur et du modèle, vous devez mettre cette ligne avant le message est enregistré. Si vous le mettez après, vous obtiendrez un message d'erreur comme celui-ci:

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

Avec la version RSpec 3+

Code réel contenant une seule invocation de Rails.logger.error:

Rails.logger.error "Some useful error message"

Code de spécification:

expect(Rails.logger).to receive(:error).with(/error message/)

Si vous souhaitez que le message d'erreur soit réellement enregistré pendant l'exécution de la spécification, utilisez le code suivant:

expect(Rails.logger).to receive(:error).with(/error message/).and_call_original

Code réel contenant plusieurs invocations de Rails.logger.error:

Rails.logger.error "Technical Error Message"
Rails.logger.error "User-friendly Error Message"

Code de spécification:

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

Aussi si vous vous souciez de faire correspondre le premier message et non les messages suivants, vous pouvez utiliser les suivants

  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

Remarque dans le paramètre de variation ci-dessus .ordered est important sinon les attentes définies commencent à échouer.

Les références:

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

Si votre objectif est de tester la fonctionnalité de journalisation, vous pouvez également envisager de vérifier la sortie vers des flux standard.

Cela vous épargnera le processus de moquerie et testera si les messages finiront réellement là où ils étaient censés (STDOUT/STDERR).

Avec RSpec's output matcher (introduit dans 3.0), vous pouvez faire ce qui suit:

expect { my_method }.to output("my message").to_stdout
expect { my_method }.to output("my error").to_stderr

Dans le cas de bibliothèques telles que Logger ou Logging, vous devrez peut-être utiliser output.to_<>_from_any_process.

5
thisismydesign