I'm trying to mock a class, so that I can expect it is instantiated and that a certain method is then called.
I tried:
expect(MyPolicy).
to receive(:new).
and_wrap_original do |method, *args|
expect(method.call(*args)).to receive(:show?).and_call_original
end
But all I'm getting is:
undefined method `show?' for #RSpec::Mocks::VerifyingMessageExpectation:0x0055e9ffd0b530
I've tried providing a block and calling the original methods first (both :new and :show?, which I had to bind first), but the error is always the same.
I know about expect_any_instance_of, but it's considered code-smell, so I'm trying to find another way to do it properly.
Context: I have pundit policies and I want to check whether or not it has been called
I also tried, with the same error:
ctor = policy_class.method(:new)
expect(policy_class).
to receive(:new).
with(user, record) do
expect(ctor.call(user, record)).to receive(query).and_call_original
end
You broke
MyPolicy.new.Your wrapper for
newdoes not return a new MyPolicy object. It returns the result ofexpect(method.call(*args)).to receive(:show?).and_call_originalwhich is a MessageExpectation.Instead, you can ensure the new object is returned with
tap.Or do it the old fashioned way.
It is often simpler to separate the two steps. Mock MyPolicy.new to return a particular object and then expect the call to show? on that object.
This does mean MyPolicy.new always returns the same object. That's an advantage for testing, but might break something. This is more flexible since it separates the scaffolding from what's being tested. The scaffolding can be reused.
Finally, inaccessible constructor calls are a headache both for testing, and they're inflexible. It's a default which cannot be overridden.
Turn it into an accessor with a default.
Now it can be accessed in the test or anywhere else.
This is the most robust option.