禁止在原始对象上未定义的模拟方法

时间:2014-01-29 08:53:38

标签: ruby-on-rails-3 rspec mocking

我想确保我的EventNotifier对象捕获并将异常记录到Rails记录器中,因此我使用rspec-mocks编写了以下rspec测试:

describe EventNotifier do
  before(:all) do
    RSpec::Mocks::setup(self)
  end

  it 'logs exception on error' do
    error = RuntimeError.new('simulated for tests')

    EventDispatcher.should_receive(:dispatch).with('a message').and_raise(error)
    Rails.logger.should_receive(:warning).with(error)

    subject.notify('a message')
  end
end

class EventDispatcher
  def dispatch(message)
    # stuff
  end
end

class EventNotifier
  def notify(message)
    EventDispatcher.dispatch(message)
  rescue RuntimeError => e
    Rails.logger.warning(e)
  end
end

测试通过了铃声和口哨,但我犯了错误。你能发现它吗?实际上,记录警告的Rails方法是warn,而不是warning。因此,当用于实际代码时,此代码将失败。

有没有办法让rspec-mock禁止原始对象上不存在的模拟方法?

另一个有用的例子:如果我决定将EventDispatcher#dispatch重命名为EventDispatcher#route,那么测试仍会通过。如果它是java,它将在编译时失败,因为接口已更改。使用rspec-mock,我不知道如何让它自动失败。

1 个答案:

答案 0 :(得分:0)

rspec-mocks 3.0可以引入verifying doubles

  

验证双打是提供正常双打的更严格的替代方案   关于正在验证的内容的保证。 使用验证双打时,RSpec   将检查被存根的方法是否实际存在于   基础对象(如果可用)。更喜欢使用验证双打   正常的双打。

所以给定这些类(来自spec的例子):

class ConsoleNotifier
  def notify(msg)
    puts message
  end
end

class User < Struct.new(:notifier)
  def suspend!
    notifier.notify("suspended as")
  end
end

此示例将通过,因为实际notify实例存在方法ConsoleNotifier

describe User, '#suspend!' do
  it 'notifies the console' do
    notifier = instance_double("ConsoleNotifier")

    expect(notifier).to receive(:notify).with("suspended as")

    user = User.new(notifier)
    user.suspend!
  end
end

如果将notify方法重命名为publish,则该示例将失败,因为期望在方法notify上,并且该方法在目标对象上不存在。

# would make example fail
class ConsoleNotifier
  def publish(msg)
    puts message
  end
end

如果我们在color方法中添加notify参数,它也会失败,因为方法arity在期望中是错误的(参数数量错误)。

class ConsoleNotifier
  def notify(msg, color)
    puts color + message
  end
end

在对课程提出期望时,例如EventDispatcher.should_receive(:dispatch)expect(EventDispatcher).to receive(:dispatch),创建的double在rspec-mocks词汇表中命名为partial double

要使部分双打像验证双打一样,必须设置verify_partial_doubles配置选项:

RSpec.configure do |config|
  config.mock_with :rspec do |mocks|
    mocks.verify_partial_doubles = true
  end
end

它会自动确保EventDispatcher类具有dispatch方法。因此,如果重命名此方法,测试将失败,正如预期的那样!