Search code examples
rubyrspecstub

Can I stub STDERR in RSpec?


I have a simple function that I'd like to test (perhaps mostly to appease simplecov). The function is:

module Utils
  extend self

  def blather(msg)
    msg = "=== " + msg
    STDERR.puts(msg)
    Rails.logger.debug(msg)
  end

end

The RSpec documentation for stubbing says that:

Messages can be stubbed on any class, including those in Ruby's core library.

But the following:

# file: spec/lib/utils_spec.rb
require 'spec_helper'
describe Utils do
  context "blather" do
    it "should print to STDERR" do
      STDERR.any_instance.should_receive(:puts).with("=== zoo")    
      Utils.blather("zoo")
    end
  end
end

... I get an error

undefined method `any_instance' for #<IO:<STDERR>>

Putting aside questions as to whether this test makes any sense, is it possible to stub STDERR (the IO class)? Is this failing because it's a class method? Or is there a more sensible strategy for this kind of test?


Solution

  • Firstly, you should typically use $stderr rather than STDERR.

    module Utils
      extend self
    
      def blather(msg)
        msg = "=== " + msg
        $stderr.puts(msg)
        Rails.logger.debug(msg)
      end
    
    end
    

    To answer your question, you can do the following in RSpec:

    describe Utils do
      context "blather" do
        it "should print to stderr" do
          $stderr.should_receive(:puts).with("=== zoo")
          Utils.blather("zoo")
        end
      end
    end
    

    You just stub the method via $stderr.should_receive. Because $stderr is a normal object, you can stub methods on it like normal as well as set up expectations on it.