5

I have an application and I want to test if I get correct messages from my logger.

A short example (you may switch between log4r and logger):

gem 'minitest'
require 'minitest/autorun'
require 'log4r'
#~ require 'logger'
class Testlog < MiniTest::Test
  def setup
    if defined? Log4r
      @log = Log4r::Logger.new('log')
      @log.outputters << Log4r::StdoutOutputter.new('stdout', :level => Log4r::INFO)
    else
      @log = Logger.new(STDOUT)
      @log.level = Logger::INFO
    end
  end

  def test_silent
    assert_silent{ @log.debug("hello world") }
    assert_output(nil,nil){ @log.debug("Hello World") }
  end
  def test_output
    #~ refute_silent{ @log.INFO("Hello") }#-> NoMethodError: undefined method `refute_silent'        
    assert_output("INFO log: Hello World\n",''){ @log.info("Hello World") }
  end

end

But I get:

  1) Failure:
Testlog#test_output [minitest_log4r.rb:27]:
In stdout.
Expected: "INFO log: Hello World\n"
  Actual: ""

On my output screen I see the message. I have similar results with Log4r::StderrOutputter and Log4r::Outputter.stdout.

So it seems it is send to the output screen, but it is not catched by minitest in STDOUT or STDERR.

Before I start to write a minitest-log4r-Gem:

Is there a possibility to test logger-output in minitest?


If not: Any recommendations how to implement a minitest-log4r-Gem?

Examples what I could imagine:

  • define new outputter for minitest (Log4r::MinitestOutputter)
  • Mock the logger.
  • new assertions (add the new outputter as parameter?):
    • assert_message('INFO log: Hello World'){ @log.info("Hello World") }
    • assert_messages(:info => 1, :debug => 2){ @log.info("Hello World") } to count messages.
    • assert_info_messages('Hello World'){ @log.info("Hello World") }
    • assert_debug_messages('Hello World'){ @log.info("Hello World") }
knut
  • 27,320
  • 6
  • 84
  • 112

4 Answers4

3

You can set up a pipe, pass the writer from the pipe to the logger, and then use the reader from the pipe to test your assertions.

http://ruby-doc.org/core-2.1.0/IO.html#method-c-pipe

Something like:

require 'logger'
r, w = IO.pipe
log = Logger.new(w)
log.info "testing info log message"
output = r.gets
puts "Test passed: #{!!(/testing/ =~ output)}"
datashaman
  • 8,301
  • 3
  • 22
  • 29
Puhlze
  • 2,634
  • 18
  • 16
2

In meantime I created a minitest-logger-Gem

A code example how to use it:

require 'log4r'
require 'minitest-logger'

class Test_log4r < MiniTest::Test
  def setup 
    @log = Log4r::Logger.new('log')
    @log.level = Log4r::INFO
  end    
  def test_output_1 
    assert_log(" INFO log: Hello World\n"){ @log.info("Hello World") }
  end
  def test_output_regex
    assert_log(/Hello World/){ @log.info("Hello World") }
  end  

  def test_silent
    assert_silent_log(){
      @log.debug("Hello World")
      #~ @log.info("Hello World")     #uncomment this to see a failure
    }
    refute_silent_log(){
      @log.warn("Hello World")     #comment this to see a failure
    }
  end

end

During the test a temporary outputter is added to the logger @log. After the test the outputter is removed again.

The gem supports log4r and logger.

knut
  • 27,320
  • 6
  • 84
  • 112
0

@Puhlze answer is good. Just for non-blocking, check before hand if there is input available:

if IO.select([r], [], [], 0.01).nil?
dev-Bilal
  • 1
  • 1
0

Suppose we have this code here on a file called logger.rb:

require 'logger'

class Framework
  attr_reader :logger

  def initialize
    @logger = Logger.new("/tmp/minitest.log")
  end
end

class Custom
  def initialize(framework)
    @framework = framework
  end

  def error!
    raise StandardError, 'Forced error'
  rescue StandardError => e
    @framework.logger.error "Error: #{e}"
  end
end

And we need to test the logger error messages. We can use a stub method and a StringIO object:

require 'minitest'
require 'minitest/autorun'
require_relative 'logger.rb'

class LoggerTest < MiniTest::Test
  def test_logger
    framework = Framework.new
    custom    = Custom.new(framework)
    io        = StringIO.new

    framework.stub :logger, Logger.new(io) do
      custom.error!
      assert_match(/Forced error/, io.string)
    end
  end
end

This way we don't need to override the framework logger, just stub it.

taq
  • 31
  • 1
  • 3