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
Detect When a Devise Session Expires
Error:'Incompatible Library Version' SQLite3-1.3.11 in Rails
Ruby/Rails - How to Validate Against Decimal Scale
Ssl_Connect Returned=1 Errno=0 State=Sslv3 Read Server Certificate B: Certificate Verify Failed MAC
Ruby Is Already Using the Class Name of My Model
Ruby on Rails: Params Is Nil. Undefined Method '[]' for Nil:Nilclass
Ruby Error After Installing Heroku Toolbelt
Rails Cancan and State MAChine - Authorizing States
What Do Ruby's Printf Arguments Mean
Nesting Too Deep' Error While Retrieving JSON Using Httparty
Exclude Draft Articles from Solr Index with Sunspot
Comparing Identical Datetime Objects in Ruby -- Why Are These Two Datetime.Now's Not Equal
How to Mock an Instance Method of an Already Mocked Object
How to Convert 1 to "First", 2 to "Second", and So On, in Ruby