2012-06-12 8 views
42

Ich versuche zu testen, dass der Rails-Logger Nachrichten in einigen meiner Spezifikationen empfängt. Ich benutze die Logging gem.RSpec: wie Rails Logger Nachricht Erwartungen zu testen?

Lasst uns sagen, dass ich eine Klasse wie dieses:

class BaseWorker 

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

end 

Und eine spec wie:

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 

erhalte ich die Meldung nach dem Ausfall:

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

Ich habe habe verschiedene Ansätze ausprobiert, um die Spezifikation zu bestehen. Das funktioniert zum Beispiel:

Aber eine zugängliche Instanzvariable wie diese einzurichten, scheint so, als würde der Schwanz hier mit dem Hund wedeln. (Eigentlich bin ich nicht einmal sicher, warum das Kopieren von Logger zu @log würde es passieren.)

Was ist eine gute Lösung für das Testen der Protokollierung?

+1

Diese Frage auf SO mehrmals hat ergeben, siehe zum Beispiel [hier] (http://stackoverflow.com/questions/153234/how-deep-are-your-unit-tests) und [hier] (http: //stackoverflow.com/questions/1168151/unit-testing-logging-and-dependency-injection) und der allgemeine Konsens war, dass Sie nicht die Protokollierung testen, es sei denn es eine Projektanforderung ist. –

+1

Art, danke für den Kommentar. Ich habe diese gelesen. Das könnte die ultimative Antwort sein. – keruilin

Antwort

83

Während ich zustimme, dass Sie in der Regel nicht Logger testen möchten, gibt es Zeiten, die es nützlich sein kann.

Ich hatte Erfolg mit den Erwartungen an Rails.logger.

RSpec ist veraltet should-Syntax:

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

RSpec neueren expect-Syntax:

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

Hinweis: In Controller und Modell-Spezifikationen, müssen Sie diese Zeile setzen vor der Nachricht wird protokolliert.Wenn Sie es nach setzen, erhalten Sie eine Fehlermeldung wie diese:

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

Ich habe den ähnlichen Fall erwarte, dass meine erwartete String einen Teilstring wird, konnte ich nicht so weit herausfinden, wie man mit ihm, jede Hilfe umgehen? –

+3

@AmolPujari 'erwarten (Rails.logger) .to erhalten (: info).mit (/ partial_string /) ' wo" partial_string "ist die Zeichenfolge, die Sie suchen. Simple Regex vergleichen – absessive

+0

Das ist großartig, ich überprüfe, dass ich * überhaupt nichts bekomme * in Fehler protokolliert und gegen Rspecs alles überprüft Matcher macht das schön: 'expect (Rails.logger) .to_not receive (: error) .with (alles) ' –

11

Mit RSpec 3+ Version

Actual Code enthält einzigen Aufruf von Rails.logger.error:

Rails.logger.error "Some useful error message" 

Spec code:

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

Wenn Sie möchten Fehlermeldung tatsächlich angemeldet sein, während die spec läuft dann folgenden Code verwenden:

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

tatsächlichen Code mehr Aufrufe von Rails.logger.error enthält:

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

Spec-Code:

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

Hinweis in der obigen Variation Einstellung .ordered ist wichtig, sonst Erwartungen s et starten fehlgeschlagen.

In Rails Zusammenhang habe ich den obigen Code überprüft, wie erwartet arbeitet jedoch mit info und debug Ebenen es scheint nicht in einfacher Weise zu arbeiten. Ich denke, seine wegen Rails intern Debug- und Informationen Ebenen verwendet, die Fehler wie

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

Referenzen verursacht werden können:

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

Wenn Ihr Ziel ist Logging-Funktionalität zu testen Sie können auch die Ausgabe in Standardstreams überprüfen.

Dies wird Ihnen den spöttischen Prozess ersparen und testen, ob die Nachrichten tatsächlich am Ende, wo sie (STDOUT/STDERR) angenommen wird.

Mit RSpec der output matcher (eingeführt in 3.0) können Sie wie folgt vorgehen:

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

Bei Bibliotheken wie Logger oder Logging Sie müssen output.to_<>_from_any_process verwenden.

Verwandte Themen