How to Mock Super in Ruby Using Rspec

How to mock my superclass in rspec

In general you shouldn't do it. A better approach (IMHO) is to make some slight changes to your class definitions

class Blurk
def initialize
# horror
end

def perform
# yet more horror
exec_perform
end

protected

def exec_perform
raise "exec_perform must be overriden"
end
end

class Grunf < Blurk
def exec_perform
# here some code to test
end
end

and to test Blurk and Grunf separately (in Blurk tests you may create TestClass definition in order to confirm exec_perform is run as expected). In Grunf's test file you would just test the exec_perform method.

RSpec mock or stub super in a model

You can check if the method is called on the 'super' class

ActionDispatch::Integration.any_instance.should_receive(:post)

Since ApiDock is only required for your tests you could also overwrite the post method with alias_method_chain:

ActionDispatch::Integration.instance_eval do
def post_with_apidoc(path, parameters = nil, headers = nil)
post_without_apidoc
if ENV['API_DOC'] == "true"
document_request("post", path, parameters, headers)
end
end
alias_method_chain :post, :apidoc
end

Testing ruby superclass inherited functionality with rspec

I'm just going to jump to the most concrete part of your question, about how to avoid using a global variable to pass a local parameter to the dummy class instantiated in your spec.

Here's your spec code:

let(:test_subclass) do
# this feels like an anti-pattern, but the Class.new block scope
# doesn't contain config_parameter from the Rspec describe

$config_parameter = config_parameter

Class.new(Superclass) do
configuration_parameter $config_parameter
end
end

If you take the value returned from Class.new you can call configuration_parameter on that with the local value and avoid the global. Using tap does this with only a minor change to your existing code:

let(:test_subclass) do
Class.new(SuperClass).tap do |klass|
klass.configuration_parameter config_parameter
end
end

As to the more general question of how to test functionality inherited from a superclass, I think the general approach of creating a stub subclass and writing specs for that subclass is fine. I personally would make your _configuration_parameter class attribute private, and rather than testing that the configuration_parameter method actually sets the value, I'd instead focus on checking that the value is different from the superclass value. But I'm not sure that's in the scope of this question.

How to mock a Rails subclass contant in Rspec?

I don't see the issue with using FakeController2 or any other name, but the only public alternative I can think of is to introduce a module so that your second FakeController exists in a different namespace, as in:

module Foo
class FakeController < ApplicationController
end
end

There is a private method remove_const defined on Kernel which can be used to unregister a constant from an object. So, if FakeController is defined on Object, you can unregister it with the call:

Object.send(:remove_const, :FakeController)

At that point, you can define the constant again as you would if it had never never been defined in the first place. (Remember: Ruby is an interpreted language.)

Stub super method in controller

in Ruby super looks like a method, but it's actually a keyword with special behavior (for example, super and super() do different things, unlike every other Ruby method), and you can't stub it.

What you really want to do is stub the method that super invokes, which in this case is ImportBackend#import_from_file. Since it's a mixin from a module (and not a superclass) you can't stub it in the usual way. You can, however, define a dummy module that has the mock behavior you want, and include it in your class. This works because when multiple modules define a mixin, super will invoke the last one included. You can read more about this approach here. In your case, it would look something like this:

mock_module = Module.new do
def import_from_file
raise Transfer::Error
end
end

controller.singleton_class.send(:include, mock_module)

Depending on your other specs, this could introduce some complications with teardown, but I hope it helps get you started.

Ruby Rspec Mock Same instance to behave different in the first and second call

Yes, you can tell RSpec mocks to return different values on each call. The following example would return false on the first call and true on all later calls:

allow(@verify).to receive(:sucess?).and_return(false, true)

How to integrate that into your test depend on how you set @verify and how your tests look in general.



Related Topics



Leave a reply



Submit