9

Is it somehow possible to test warnings i Ruby using RSpec?

Like this:

class MyClass
  def initialize
    warn "Something is wrong"
  end
end

it "should warn" do
  MyClass.new.should warn("Something is wrong")
end
Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
Linus Oleander
  • 17,746
  • 15
  • 69
  • 102

4 Answers4

18

warn is defined in Kernel, which is included in every object. If you weren't raising the warning during initialization, you could specify a warning like this:

obj = SomeClass.new
obj.should_receive(:warn).with("Some Message")
obj.method_that_warns

Spec'ing a warning raised in the initialize method is quite more complex. If it must be done, you can swap in a fake IO object for $stderr and inspect it. Just be sure to restore it after the example

class MyClass
  def initialize
    warn "Something is wrong"
  end
end

describe MyClass do
  before do
    @orig_stderr = $stderr
    $stderr = StringIO.new
  end

  it "warns on initialization" do
    MyClass.new
    $stderr.rewind
    $stderr.string.chomp.should eq("Something is wrong")
  end

  after do
    $stderr = @orig_stderr
  end
end
David Chelimsky
  • 8,920
  • 2
  • 38
  • 30
  • Could you do `SomeClass.allocate` rather than `SomeClass.new`, and then give it the should_receive, and then run initialize? – Andrew Grimm Oct 20 '11 at 06:08
  • The other approach I've used for warnings in `initialize` is to have my class call `Kernel.warn` explicitly (rather than just `warn`). It doesn't need to be called on kernel; it just need to be called on some global that I can set a `should_receive` on before instantiation. – Myron Marston Nov 11 '11 at 16:46
6

There is a good article with custom expectation which solves exactly your problem: http://greyblake.com/blog/2012/12/14/custom-expectations-with-rspec/

So it would like:

expect { MyClass.new }.to write("Something is wrong").to(:error)

Base on that article you can create you own expectation to use it like this:

expect { MyClass.new }.to warn("Something is wrong")
Sergey Potapov
  • 3,819
  • 3
  • 27
  • 46
  • 2
    This is a super awesome answer but I would recommend putting the bulk of the article in the answer in case the article goes down. – sunnyrjuneja Mar 10 '14 at 07:10
0

If warning raises during initialization you can use output expectation:

expect do
  described_class.new
end.to output('test message').to_stderr
Urka
  • 65
  • 1
  • 7
-1

This is my solution, I define a custom matcher has_warn

require 'rspec'
require 'stringio'

module CustomMatchers
  class HasWarn
    def initialize(expected)
      @expected = expected
    end

    def matches?(given_proc)
      original_stderr = $stderr
      $stderr = StringIO.new
      given_proc.call
      @buffer = $stderr.string.strip
      @expected.include? @buffer.strip
    ensure
      $stderr = original_stderr
    end

    def supports_block_expectations?
      true
    end

    def failure_message_generator(to)
      %Q[expected #{to} get message:\n#{@expected.inspect}\nbut got:\n#{@buffer.inspect}]
    end

    def failure_message
      failure_message_generator 'to'
    end

    def failure_message_when_negated
      failure_message_generator 'not to'
    end

  end

  def has_warn(msg)
    HasWarn.new(msg)
  end
end

now you can use this function as follow after include the CustomMatchers:

expect{ MyClass.new }.to has_warn("warning messages")
ryan
  • 847
  • 1
  • 10
  • 18