mock allow logging rspec mocking stubbing

logging - allow - rspec mock ruby



RSpec: ¿cómo probar las expectativas del mensaje del registrador de rieles? (3)

Estoy intentando probar que el registrador Rails recibe mensajes en algunas de mis especificaciones. Estoy usando la gema de registro .

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

Recibo 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

He probado varios enfoques diferentes para que la especificación pase. 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 como esa parece que la cola mueve al perro aquí. (En realidad, ni siquiera estoy seguro de por qué copiar logger a @log lo haría pasar).

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


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 .

Usar la sintaxis obsoleta de RSpec:

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

Utilizando la sintaxis de expect más nueva de RSpec:

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

Nota: en las especificaciones del controlador y del modelo, debe colocar esta línea antes de que se registre el mensaje. Si lo pones después, recibirás 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


Con la versión RSpec 3+

Código real que contiene una sola invocación de Rails.logger.error :

Rails.logger.error "Some useful error message"

Código de especificación:

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

Si desea que el mensaje de error se registre realmente mientras se ejecuta la especificación, utilice el 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 especificación:

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

Tenga en cuenta que la configuración de variación anterior. .ordered es importante, de lo contrario, las expectativas establecidas comienzan a fallar.

En el contexto de Rails, he verificado que el código anterior funciona como se esperaba; sin embargo, con los niveles de info y debug no parece funcionar de manera directa. Supongo que es debido a que Rails usa internamente niveles de depuración e información que pueden estar causando 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


Si su objetivo es probar 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 el marcador de salida RSpec (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 bibliotecas como Logger o Logging es posible que deba usar output.to_<>_from_any_process .