Search code examples
rubyrspecrackrack-test

Gathering Logs within Rack::Test


I have a rack application (specifically a Sinatra one, but I don't think that matters) which, when running normally, happily outputs a bunch of information about the requests which are being made of it to STDOUT eg:

127.0.0.1 - - [25/Jul/2013 10:05:39] "GET /oath2/token?password=ohnoes HTTP/1.1" 404 507 0.0013

I'm trying write an extension for Rack::CommonLogger that will remove passwords from the logfile, so of course my first task is to write a test.

I have rack/test set up with rspec like so, but I can't figure out how to capture the outgoing logs so I can scan what's in them! Any ideas?

require 'my_webapp'

describe "My Webapp" do
  include Rack::Test::Methods

  def app
    @app ||= MyWebapp.new
  end

  it 'should not log the text of any GET password parameter' do
    get '/oauth2/token?password=ohnoes'

    # Not sure about this!

    log_output.should_not =~ /ohnoes/
  end
end

Solution

  • You need to stub and set expectations on whatever method your custom logger is calling to write its output.

    So, for example, if your custom logger writes directly to STDERR with a single argument, you would use:

    STDERR.stub(:write) {|arg| expect(arg).to_not match(/password/)}
    

    If your custom logger writes to the default logger (i.e. env['rack.errors']) instead of STDERR, then you would stub the write method of that object instead. I would have shown that example, but I can't figure out how to get a hold of the Rack environment within an RSpec test.

    A couple of notes on this implementation:

    • The use of any_number_of_times, as in STDERR.should_receive(:write).any_number_of_times is deprecated in favor of stub
    • There is no reasonable regex to match strings which do not contain a particular string, so there's no reasonable way to use the form STDERR.stub(:write).with(/regex/)