Suprimiendo salida a consola con Ruby.

Estoy escribiendo algunas pruebas unitarias como las siguientes:

def executing_a_signal a_method(a_signal.new, a_model, a_helper); assert_equal(new_state, a_model.state) end 

Las pruebas funcionan bien, pero el método que se ejecuta justo antes de la afirmación para ejecutar la lógica imprime varios mensajes a la consola, principalmente a través de puts .

¿Hay una forma rápida, quizás incorporada, de suprimir esa salida a la consola? Solo me interesa el efecto final del método en el objeto modelo y, básicamente, para mantener la consola limpia básicamente, esperaba encontrar una manera de evitar simplemente todos los resultados a la consola sin volver a escribir o comentarlos. puts declaraciones solo para mis pruebas.

Definitivamente no es un problema crítico, pero me gustaría mucho escuchar cualquier pensamiento o idea (o solución) sobre él.

Uso el siguiente fragmento de código en las pruebas para capturar y probar STDOUT

 def capture_stdout(&block) original_stdout = $stdout $stdout = fake = StringIO.new begin yield ensure $stdout = original_stdout end fake.string end 

Con este método, lo anterior se convertiría en:

 def executing_a_signal capture_stdout { a_method(a_signal.new, a_model, a_helper) } assert_equal(new_state, a_model.state) end 

Hay dos soluciones: redirigir a dónde se puts escrituras (la solución dada por @cldwalker más arriba), o sobrescribir el método de las mismas para que sea un no-op. (La implementación debe ser obvia: module Kernel; def puts(*args) end end ).

Sin embargo, en este caso, lo que realmente sería la mejor solución es “escuchar sus pruebas”. Porque, muchas veces, cuando algo es difícil de probar, sus pruebas realmente están tratando de decirle que algo está mal con su diseño. En este caso, huelo una violación del Principio de Responsabilidad Única: ¿por qué diablos necesita un objeto Modelo saber cómo escribir en la consola? ¡Su responsabilidad es representar un concepto de dominio, no registrar! ¡Para eso son los objetos Logger!

Por lo tanto, una solución alternativa sería que el objeto Modelo delegue la responsabilidad del registro en un objeto Logger y use la dependency injection para inyectar el objeto Model con un objeto Logger adecuado. De esa manera, simplemente puede inyectar un registrador falso para la prueba. Aquí hay un ejemplo:

 #!/usr/bin/env ruby class SomeModel def initialize(logger=Kernel) @logger = logger end def some_method_that_logs; @logger.puts 'bla' end end require 'test/unit' require 'stringio' class TestQuietLogging < Test::Unit::TestCase def setup; @old_stdout, $> = $>, (@fake_logdest = StringIO.new) end def teardown; $> = @old_stdout end def test_that_default_logging_is_still_noisy SomeModel.new.some_method_that_logs assert_equal "bla\n", @fake_logdest.string end def test_that_logging_can_be_made_quiet fake_logger = Object.new def fake_logger.puts *args; end SomeModel.new(fake_logger).some_method_that_logs assert_equal '', @fake_logdest.string end end 

Como mínimo, el objeto Modelo debe tomar el objeto IO que se está registrando como un argumento, para que pueda simplemente inyectar StringIO.new en él para la prueba:

 #!/usr/bin/env ruby class SomeModel def initialize(logdest=$>) @logdest = logdest end def some_method_that_logs; @logdest.puts 'bla' end end require 'test/unit' require 'stringio' class TestQuietLogging < Test::Unit::TestCase def setup; @old_stdout, $> = $>, (@fake_logdest = StringIO.new) end def teardown; $> = @old_stdout end def test_that_default_logging_is_still_noisy SomeModel.new.some_method_that_logs assert_equal "bla\n", @fake_logdest.string end def test_that_logging_can_be_made_quiet fake_logdest = (@fake_logdest = StringIO.new) SomeModel.new(fake_logdest).some_method_that_logs assert_equal '', @fake_logdest.string assert_equal "bla\n", fake_logdest.string end end 

Si aún desea poder simplemente decir ” puts whatever en su modelo o tiene miedo de que alguien se olvide de llamar ” puts al objeto del registrador, puede proporcionar su propio método de “privado”:

 class SomeModel def initialize(logdest=$>) @logdest = logdest end def some_method_that_logs; puts 'bla' end private def puts(*args) @logdest.puts *args end end 

Una versión ligeramente más limpia de la solución @ cldwalker:

 def silenced $stdout = StringIO.new yield ensure $stdout = STDOUT end silenced do something_that_prints end 

reopen '/dev/null'

Otra opción es redirigir a /dev/null con:

 STDOUT.reopen('/dev/null', 'w') STDERR.reopen('/dev/null', 'w') 

Esta técnica se utiliza en WEBrick::Daemon of stdlib (fuente de alternancia).

Tiene la ventaja de ser más eficiente que StringIO.new ya que no almacena la StringIO en un StringIO , pero es menos portátil.

Acabo de usar el siguiente código al principio de mi archivo .rb … así que deshabilita todas las declaraciones de impresión de la consola …

  def puts(*args) end 
    Intereting Posts