RSpec: comment tester les attentes des messages de journalisation Rails?

J’essaie de tester que l’enregistreur Rails reçoit des messages dans certaines de mes spécifications. J’utilise la 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 

Je reçois 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 les spécifications. 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 être la queue qui remue le chien ici. (En fait, je ne suis même pas sûr de savoir pourquoi copier Logger dans @log le ferait passer.)

Quelle est la bonne solution pour tester la journalisation?

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

J’ai eu du succès avec les attentes sur Rails.logger .

Utiliser la syntaxe obsolète de RSpec:

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

En utilisant la nouvelle syntaxe expect RSpec:

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

Remarque: Dans les spécifications de contrôleur et de modèle, vous devez placer cette ligne avant que le message ne soit consigné. Si vous le mettez après, vous aurez un message d’erreur comme celui-ci:

 Failure/Error: expect(Rails.logger).to receive(:info).with("some message") (#).info("some message") expected: 1 time with arguments: ("some message") received: 0 times 

Avec la version RSpec 3+

Code réel contenant un appel unique de Rails.logger.error :

 Rails.logger.error "Some useful error message" 

Code Spec:

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

Si vous souhaitez que le message d’erreur soit réellement consigné pendant que la spécification s’exécute, utilisez le code suivant:

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

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

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

Code Spec:

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

Notez que dans le paramètre de variation ci-dessus, .ordered est important, sinon les attentes commencent à échouer.

Dans le contexte Rails, j’ai vérifié que le code ci-dessus fonctionne comme prévu, mais avec les niveaux d’ info et de debug , il ne semble pas fonctionner de manière simple. Je suppose que c’est à cause de Rails en interne en utilisant des niveaux de débogage et d’information qui peuvent causer des erreurs comme

 (#).info(*(any args)) expected: 1 time with any arguments received: 4 times with any arguments 

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

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

Cela vous épargnera le processus moqueur et testera si les messages finiront effectivement par où ils sont censés (STDOUT / STDERR).

Avec le matcher de sortie de RSpec (introduit en 3.0), vous pouvez effectuer les opérations suivantes:

 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 .