2012-06-12 15 views
42

Próbuję przetestować, czy program rejestrujący Railsy odbiera wiadomości w niektórych moich specyfikacjach. Używam Logging gem.RSpec: w jaki sposób przetestować oczekiwanie na komunikat Logger Rails?

Załóżmy, że mam klasy jak poniżej:

class BaseWorker 

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

end 

oraz specyfikację jak:

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 

pojawia się następujący komunikat o awarii:

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

mam wypróbował kilka różnych podejść, aby uzyskać specyfikację. Działa to na przykład:

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 

Ale ustawienie takiej dostępnej zmiennej instancji wygląda na to, że ogon macha tu psem. (Właściwie to nie jestem nawet pewien, dlaczego skopiowanie logger'a do @log sprawi, że przejdzie.)

Co jest dobrym rozwiązaniem do testowania rejestrowania?

+1

To pytanie pojawiło się kilka razy na SO, zobacz na przykład [tutaj] (http://stackoverflow.com/questions/153234/how-deep-are-your-unit-tests) i [tutaj] (http: //stackoverflow.com/questions/1168151/unit-testing-logging-and-dependency-injection) i ogólnie uważano, że nie testuje się rejestrowania, chyba że jest to wymóg projektu. –

+1

Sztuka, dzięki za komentarz. Czytałem te. To może być ostateczna odpowiedź. – keruilin

Odpowiedz

83

Podczas gdy zgadzam się, że ogólnie nie chcesz testować rejestratory, są chwile, może być przydatne.

Odniosłem sukces z oczekiwaniami na Rails.logger.

Korzystanie rspec za przestarzałe should składnię:

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

Korzystanie rspec za nowszą expect składnię:

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

Uwaga: kontroler i modelowych specyfikacji, trzeba umieścić tę linię przed na wiadomość jest zarejestrowana.Jeśli umieścisz go po, otrzymasz komunikat o błędzie, jak poniżej:

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

Mam podobny przypadek, oczekując, że mój oczekiwany ciąg jest częściowym ciągiem znaków, nie mogłem wymyślić jak dotąd, jak sobie z nim poradzić, jakiejkolwiek pomocy? –

+3

@AmolPujari 'spodziewać (Rails.logger) .do odbioru (: informacje).z (/ partial_string /) ' gdzie" partial_string "jest szukanym ciągiem znaków. Prosty Regex porównaj – absessive

+0

To jest świetne, sprawdzam, nie dostaję * niczego w ogóle * zalogowałem się do błędu i sprawdzając z Rspec wszystko, co Matcher to ładnie: 'oczekiwać (Rails.logger) .to_nie otrzymywać (: błąd). (cokolwiek) ' –

11

Z rspec 3+ wersji

Rzeczywisty kod zawierający pojedynczy pw Rails.logger.error:

Rails.logger.error "Some useful error message" 

Spec kod:

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

Jeśli chcesz Komunikat o błędzie być faktycznie zalogowany natomiast biegnie Spec następnie użyć następującego kodu:

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

rzeczywisty kod zawierający wiele wywołań od Rails.logger.error:

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

kod Spec:

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

Uwaga w powyższym wariancie ustawienie .ordered jest ważne inne niż oczekiwania et start failing.

W kontekście Rails I sprawdzeniu powyższy kod do pracy zgodnie z oczekiwaniami, jednak ze info i debug poziomów nie wydają się działać w prosty sposób. Chyba jego ponieważ Rails wewnętrznie używając debugowania i informacją poziomów, które mogą być przyczyną błędów jak

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

Literatura:

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

Jeśli twoim celem jest, aby przetestować funkcjonalność logowania możesz także rozważyć sprawdzenie wyjścia do standardowych strumieni.

Uchroni to przed kpiną i sprawdzi, czy wiadomości rzeczywiście znajdą się tam, gdzie powinny (STDOUT/STDERR).

Z RSpec na output matcher (wprowadzone w wersji 3.0), można wykonać następujące czynności:

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

W przypadku bibliotek takich jak Logger lub Logging może trzeba użyć output.to_<>_from_any_process.

Powiązane problemy