Skip to content

Instantly share code, notes, and snippets.

@jwo
Created December 7, 2012 21:14
Show Gist options
  • Save jwo/4236585 to your computer and use it in GitHub Desktop.
Save jwo/4236585 to your computer and use it in GitHub Desktop.
Overriding puts

What the What?

During RubyOffRails office hours, I was asked how we could test that puts was called at the end of a method. Something like so:

def super_awesome_method
  #...
  puts the_thing
end

After discussing why the code was bad, we went about trying to use RSpec to assert that puts was actually called correct. One way would be to use StringIO to capture the value and ask it later, but that seemed a little sane for our tastes.

Instead, let's add a message expectation that puts received the correct method call. But we couldn't quite add a should_receive on Kernel. sooo:......

In IRB, you can simply redefine puts

>> def puts(*args)
>>   super "yo"
>>   end
=> nil
>> puts "OH HAI"
yo
=> nil
>> 

But how to do this in RSpec? Kernel.module_eval could redefine puts, but does that do us any good?

YES! If we have it raise an error, and we could send along those args that we were given and test THOSE.

Crazy? or Crazy like a fox?

def method_that_calls_puts
sum = 6 + 6
puts sum
end
describe "#method_that_calls_puts" do
it "should output the sum" do
Kernel.module_eval do
def puts(*args)
raise StandardError.new args.join("")
end
end
expect {
method_that_calls_puts
}.to raise_error(StandardError, "12")
end
end
@jwo
Copy link
Author

jwo commented Dec 7, 2012

(please don't ever do this)

@myronmarston
Copy link

Or you could just do:

def method_that_calls_puts
  sum = 6 + 6
  puts sum
end

describe "#method_that_calls_puts" do
  it "should output the sum" do
    self.should_receive(:puts).with(12)
    method_that_calls_puts
  end
end

You can always set a mock expectation as long as you can get a reference to the object that is the receiver of the message. You are calling method_that_calls_puts on self in the context of the example, so it in turn sends puts to self, and you can mock it just fine.

Alternately, if you want to mock the message on Kernel, you can do that, too:

def method_that_calls_puts
  sum = 6 + 6
  Kernel.puts sum
end

describe "#method_that_calls_puts" do
  it "should output the sum" do
    Kernel.should_receive(:puts).with(12)
    method_that_calls_puts
  end
end

...you just have to explicitly send the puts message to Kernel.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment