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
.