Ruby 使用RSpec测试警告

Ruby 使用RSpec测试警告,ruby,testing,rspec,warnings,Ruby,Testing,Rspec,Warnings,是否有可能使用RSpec测试Ruby中的警告 像这样: class MyClass def initialize warn "Something is wrong" end end it "should warn" do MyClass.new.should warn("Something is wrong") end 警告在内核中定义,它包含在每个对象中。如果在初始化过程中没有发出警告,可以指定如下警告: obj = SomeClass.new obj.should_r

是否有可能使用RSpec测试Ruby中的警告

像这样:

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

it "should warn" do
  MyClass.new.should warn("Something is wrong")
end

警告
内核
中定义,它包含在每个对象中。如果在初始化过程中没有发出警告,可以指定如下警告:

obj = SomeClass.new
obj.should_receive(:warn).with("Some Message")
obj.method_that_warns
expect { MyClass.new }.to warn("Something is wrong")
指定
initialize
方法中提出的警告相当复杂。如果必须这样做,您可以为
$stderr
交换一个伪
IO
对象并检查它。请确保在示例之后恢复它

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

有一篇带有自定义期望的好文章正好解决了您的问题:

因此,它希望:

expect { MyClass.new }.to write("Something is wrong").to(:error)
基于这篇文章,您可以创建自己的期望,以便像这样使用它:

obj = SomeClass.new
obj.should_receive(:warn).with("Some Message")
obj.method_that_warns
expect { MyClass.new }.to warn("Something is wrong")

这是我的解决方案,我定义了一个自定义匹配器

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
现在,在包含CustomMatchers后,您可以按如下方式使用此功能:

expect{ MyClass.new }.to has_warn("warning messages")

你能做
SomeClass.allocate
而不是
SomeClass.new
,然后给它应该接收的,然后运行initialize吗?我在
initialize
中用于警告的另一种方法是让我的类显式地调用
内核.warn
(而不仅仅是
warn
)。它不需要在内核上调用;它只需要在一些全局上调用,我可以在实例化之前设置一个
应该_receive
。这是一个非常棒的答案,但我建议将文章的大部分放在答案中,以防文章失败。