How to stub i18n_scope for mocking ActiveRecord::R

2019-07-02 00:03发布

问题:

I have tried this code but it raises the error: NameError: uninitialized constant RSpec::Mocks::Mock

RSpec::Mocks::Mock.stub(:i18n_scope).and_return(:activerecord)
model = double(:model, errors: double(:errors, full_messages: []))
ActiveRecord::RecordInvalid.new(model)

How can I stub i18n_scope?

回答1:

To answer your question, you have to stub RSpec::Mocks::Double because that's the class of the instance you're actually passing to ActiveRecord::RecordInvalid. However, that won't work because RSpec::Mocks::Double doesn't implement .i18n_scope. So, what I did (and I hate it) was monkey-patch the RSpec::Mocks::Double class with a dummy method.

  it "does something cool" do
    foo = MyClass.new
    class RSpec::Mocks::Double
      def self.i18n_scope
        :activerecord
      end
    end
    error_double = double(:model, errors: double(:errors,full_messages: ["blah"]))
    expect(foo).to receive(:bar).and_raise(ActiveRecord::RecordInvalid, error_double)
    ...
  end

But there's a better way of doing this by taking advantage of a block argument:

  it "does something cool" do
    foo = MyClass.new
    expect(foo).to receive(:bar) do |instance|
      instance.errors.add(:base, "invalid error")
      raise(ActiveRecord::RecordInvalid, instance)
    end
    ...
  end

This way, you're testing much closer to your application data / logic vs. hacking RSpec.

This worked perfectly for a controller test where I was rescuing from a specific error that was thrown from an #update! method in certain situations.