2012-06-12 19 views
42

Estoy tratando de probar que el registrador Rails recibe mensajes en algunas de mis especificaciones. Estoy usando el Logging gem.RSpec: cómo probar las expectativas del mensaje del registrador de rieles?

Digamos que tengo una clase como esta:

class BaseWorker 

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

end 

Y una especificación como:

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 

me sale el siguiente mensaje de error:

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

tengo probado varios enfoques diferentes para obtener la especificación para pasar. Esto funciona por ejemplo:

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 

Pero tener que configurar una variable de instancia accesible así parece que la cola mueve al perro aquí. (En realidad, ni siquiera estoy seguro de por qué copiar el registrador a @log lo haría pasar.)

¿Cuál es una buena solución para probar el registro?

+1

Esa pregunta surgió varias veces en SO, ver por ejemplo [aquí] (http://stackoverflow.com/questions/153234/how-deep-are-your-unit-tests) y [aquí] (http: //stackoverflow.com/questions/1168151/unit-testing-logging-and-dependency-injection) y el consenso general fue que no prueba el registro a menos que sea un requisito del proyecto. –

+1

Arte, gracias por el comentario. Yo leí esos. Esa puede ser la respuesta final. – keruilin

Respuesta

83

Aunque estoy de acuerdo en que generalmente no desea probar loggers, a veces puede ser útil.

He tenido éxito con las expectativas en Rails.logger.

Usando obsoleta should sintaxis de RSpec:

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

Usando nuevos expect sintaxis de RSpec:

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

Nota: en el controlador y el modelo de especificaciones, usted tiene que poner esta línea antes la el mensaje se registraSi lo pones después, recibirá un mensaje de error como este:

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

Tengo el caso similar de esperar que mi cadena esperada sea una cadena parcial, no pude entender hasta ahora, cómo manejarla, ¿alguna ayuda? –

+3

@AmolPujari 'expect (Rails.logger) .to receive (: info).con (/ partial_string /) ' donde" partial_string "es la cadena que está buscando. Simple Regex compare – absessive

+0

Esto es genial, estoy comprobando que no recibo * nada en absoluto * conectado al error y comprobando que todo lo que Matcher de Rspec hace bien: 'expect (Rails.logger) .to_not receive (: error) .con (cualquier cosa) ' –

11

Con la versión

código real que contiene una sola invocación de Rails.logger.error RSpec 3+:

Rails.logger.error "Some useful error message" 

Spec código:

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

Si desea la mensaje de error a ser realmente conectado mientras se ejecuta la especificación a continuación, utilizar siguiente código:

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

código real que contiene múltiples invocaciones de Rails.logger.error:

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

código de especificaciones:

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

Tenga en cuenta la configuración de variación anterior .ordered es importante más expectativas s y empiezas a fallar.

En el contexto rieles que han verificado el código anterior para funcionar como se espera, sin embargo, con info y debug niveles no parecen funcionar en forma directa. Supongo que es debido a los carriles internamente con niveles de depuración y la información que puede ser la causa de los errores como

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

Referencias:

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

Si su objetivo es poner a prueba la funcionalidad de registro también puede considerar verificar el resultado a las transmisiones estándar.

Esto le ahorrará el proceso de burla y probará si los mensajes realmente terminarán donde deberían (STDOUT/STDERR).

Con RSpec de output matcher (introducido en 3.0), puede hacer lo siguiente:

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

En el caso de las bibliotecas como Logger o Logging puede que tenga que utilizar output.to_<>_from_any_process.

Cuestiones relacionadas