2012-06-12 7 views
42

Sto provando a verificare che il registratore di Rails riceva messaggi in alcune delle mie specifiche. Sto usando il Logging gem.RSpec: come testare le aspettative dei messaggi del registratore di Rails?

Diciamo che ho una classe come questa:

class BaseWorker 

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

end 

E una specifica come:

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 

ottengo il seguente messaggio di errore:

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

ho ho provato diversi approcci per far passare le specifiche. Questo funziona per esempio:

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 

Ma avere per impostare una variabile di istanza accessibile come quella sembra la coda è agita il cane qui. (In realtà, non sono nemmeno sicuro del motivo per cui copiare il logger su @log lo farebbe passare.)

Qual è una buona soluzione per testare il logging?

+1

Questa domanda è sorta più volte su SO, si veda ad esempio [qui] (http://stackoverflow.com/questions/153234/how-deep-are-your-test-tests) e [qui] (http: //stackoverflow.com/questions/1168151/unit-testing-logging-and-dependency-injection) e il consenso generale era che non testare la registrazione a meno che non si tratti di un requisito di progetto. –

+1

Arte, grazie per il commento. Ho letto quelli. Questa potrebbe essere la risposta definitiva. – keruilin

risposta

83

Mentre sono d'accordo che in genere non si desidera testare i logger, ci sono momenti in cui può essere utile.

Ho avuto successo con le aspettative su Rails.logger.

Utilizzando deprecato should sintassi del RSpec:

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

Utilizzando più recente expect sintassi del RSpec:

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

Nota: Nel controller e modello specifiche, bisogna mettere questa linea prima la messaggio è registratoSe lo metti dopo, si otterrà un messaggio di errore simile a questo:

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 
+0

Ho il caso simile aspettatevi che la mia stringa attesa sia una stringa parziale, non sono riuscito a capire fino a ora, come gestirlo, qualche aiuto? –

+3

@AmolPujari 'expect (Rails.logger) .per ricevere (: informazioni).con (/ partial_string /) ' dove" partial_string "è la stringa che stai cercando. Simple Regex confronta – absessive

+0

Questo è grandioso, sto verificando che non ho * nulla * registrato per errore e il controllo di Rspec su qualsiasi matcher lo fa bene: 'expect (Rails.logger) .to_not receive (: error) .with (anything) ' –

11

Con la versione

codice vero e proprio contenente un'unica invocazione Rails.logger.error RSpec 3+:

Rails.logger.error "Some useful error message" 

Spec codice:

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

Se si desidera il messaggio di errore per essere effettivamente registrato durante l'esecuzione spec quindi utilizzare seguente codice:

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

codice vero e proprio contenente più invocazioni di Rails.logger.error:

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

Codice Spec:

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

Nota nell'impostazione di variazione sopra .ordered è importante altre aspettative s e iniziare a fallire.

Nel contesto Rails Ho verificato il codice qui sopra per funzionare come previsto ma con info e debug livelli non sembra funzionare in modo diretto. Credo che la sua causa di Rails utilizzando internamente livelli di debug e informazioni che possono causare errori come

(#<ActiveSupport::Logger:0x00000006c55778>).info(*(any args)) 
    expected: 1 time with any arguments 
    received: 4 times with any arguments 

Riferimenti:

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

1

Se il vostro obiettivo è quello di testare la funzionalità di registrazione potresti anche considerare di verificare l'output in stream standard.

In questo modo si risparmia il processo di simulazione e si verifica se i messaggi finiranno effettivamente dove si suppone (STDOUT/STDERR).

Con RSpec di output matcher (introdotto nel 3.0) è possibile effettuare le seguenti operazioni:

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

In caso di librerie, come Logger o Logging potrebbe essere necessario utilizzare output.to_<>_from_any_process.