How can I mock super in ruby using rspec? - ruby

I am extending an existing library by creating a child class which extends to the library class.
In the child class, I was able to test most of functionality in initialize method, but was not able to mock super call. The child class looks like something like below.
class Child < SomeLibrary
def initialize(arg)
validate_arg(arg)
do_something
super(arg)
end
def validate_arg(arg)
# do the validation
end
def do_something
#setup = true
end
end
How can I write rspec test (with mocha) such that I can mock super call? Note that I am testing functionality of initialize method in the Child class. Do I have to create separate code path which does not call super when it is provided with extra argument?

You can't mock super, and you shouldn't. When you mock something, you are verifying that a particular message is received, and super is not a message -- it's a keyword.
Instead, figure out what behavior of this class will change if the super call is missing, and write an example that exercises and verifies that behavior.

As #myron suggested you probably want to test the behavior happening in super.
But if you really want to do this, you could do:
expect_any_instance_of(A).to receive(:instance_method).and_call_original
Assuming
class B < A
def instance_method
super
end
end
class A
def instance_method
#
end
end
Disclaimer expect_any_instance_of are a mark of weak test (see):
This feature is sometimes useful when working with legacy code, though
in general we discourage its use for a number of reasons:
The rspec-mocks API is designed for individual object instances, but
this feature operates on entire classes of objects. As a result there
are some semantically confusing edge cases. For example, in
expect_any_instance_of(Widget).to receive(:name).twice it isn't clear
whether a specific instance is expected to receive name twice, or if
two receives total are expected. (It's the former.)
Using this feature is often a design smell. It may be that your test is trying to do too much or that the object under test is too
complex.
It is the most complicated feature of rspec-mocks, and has historically received the most bug reports. (None of the core team
actively use it, which doesn't help.)

A good way to test this is to set an expectation of some action taken by the superclass - example :
class Some::Thing < Some
def instance_method
super
end
end
and the super class:
class Some
def instance_method
another_method
end
def self.another_method # not private!
'does a thing'
end
end
now test :
describe '#instance_method' do
it 'appropriately triggers the super class method' do
sawm = Some::Thing.new
expect(sawm).to receive(:another_method)
sawm.instance_method
end
end
All This Determines Is That Super Was Called On the Superclass
This pattern's usefulness is dependent on how you structure your tests/what expectations you have of the child/derivative class' mutation by way of the super method being applied.
Also - pay close attention to class and instance methods, you will need to adjust allows and expects accordingly
YMMV

A bit late to this party, but what you can also do is forego using the super keyword and instead do
class Parent
def m(*args)
end
end
class Child < Parent
alias super_m m
def m(*args)
super_m(*args)
end
end
That way your super method is accessible like any other method and can e.g. be stubbed like any other method. The main downside is that you have to explicitly pass arguments to the call to the super method.

Related

How do I bypass a call to super method in test

I have a basic structure like this
class Automobile
def some_method
# this code sets up structure for child classes... I want to test this
end
end
class Car < Automobile
def some_method
super
# code specific to Car... it's tested elsewhere so I don't want to test this now
end
end
class CompactCar < Car
def some_method
super
# code specific to CompactCar... I want to test this
end
end
What is the recommended way to test CompactCar and Automobile without running the code from Car? Automobile#some_method provides the structure that is required by child classes, so I want to always test that, but Car's functionality is tested elsewhere and I don't want to duplicate efforts.
One solution is to use class_eval to overwrite Car#some_method, but this isn't ideal because the overwritten method stays in place for the duration of my testing (unless I re-load the original library file with setup/teardown methods... kind of an ugly solution). Also, simply stubbing the call to Car#some_method does not seem to work.
Is there a cleaner/more generally accepted way of doing this?
Just put the specific code into a separate method. You don't appear to be using anything from super. Unless you are?
class CompactCar < Car
def some_method
super
compact_car_specific_code
end
# Test this method in isolation.
def compact_car_specific_code
# code specific to CompactCar... I want to test this
end
end

Why use extend/include instead of simply defining method in main object?

RSpec adds a "describe" method do the top-level namespace. However, instead of simply defining the method outside of any classes/modules, they do this:
# code from rspec-core/lib/rspec/core/dsl.rb
module RSpec
module Core
# Adds the `describe` method to the top-level namespace.
module DSL
def describe(*args, &example_group_block)
RSpec::Core::ExampleGroup.describe(*args, &example_group_block).register
end
end
end
end
extend RSpec::Core::DSL
Module.send(:include, RSpec::Core::DSL)
What is the benefit of using this technique as opposed to simply defining describe outside any modules and classes? (From what I can tell, the DSL module isn't used anywhere else in rspec-core.)
I made this change a few months ago so that describe is no longer added to every object in the system. If you defined it at the top level:
def describe(*args)
end
...then every object in the system would have a private describe method. RSpec does not own every object in the system and should not be adding describe willy-nilly to every object. We only want the describe method available in two scopes:
describe MyClass do
end
(at the top-level, off of the main object)
module MyModule
describe MyClass do
end
end
(off of any module, so you nest your describes in a module scope)
Putting it in a module makes it easy to extend onto the main object (to add it to only that object, and not every object) and include it in Module (to add it to all modules).
Actually, if that's all there is in the code, I don't really believe it to be much better — if at all. A common argument is that you can easily check that RSpec is responsible for addinng this method in the global namespace by checking the method owner. Somehow it never felt this was needed, as the location of the method already stores that information.
Defining the method outside of any scope would have be equivalent to defining a private instance method in Object:
class Object
private
def double(arg)
arg * 2
end
end
double(3) # OK
3.double(3) # Error: double is private
self.double(3) # Error: double is private
I think privateness is a useful aspect, because it prevents from making certain method calls that have no meaning, that the code shown in the question lacks.
There's an advantge to defining the method in a module, though, but the RSpec code doesn't seem to make use of it: using module_function, not only do you preserve privateness of the instance method, but you also get a public class method. This means that if you have an instance method of the same name, you will still be able to refer to the one defined by the module, by using the class method version.
A common example of module_function is the Kernel module, which contains most function-like core methods like puts (another one is Math). If you're in a class that redefines puts, you can still use Kernel#puts explicitly if you need:
class LikeAnIO
def puts(string)
#output << string
end
def do_work
puts "foo" # inserts "foo" in #output
Kernel.puts "foo" # inserts "foo" in $stdout
end
end

Overriding object attribute access

I was wondering if it's possible to make it so that if I had something like
class Test
attr_reader :access_times
def initialize
#access_times = 0
end
def get_two
2
end
...
end
t = Test.new
That any access to t would run a particular piece of code before actually running the method?
For example, if I suddenly decided to say t.get_two, the fact that I used the . syntax would increment #access_times by 1. Or perhaps I made a check t.is_a?(Test), it would also increment #access_times by 1. Accessing any methods or attributes inherited by Test would also increment the variable by 1.
Basically I want to add some stuff to the . syntax if possible.
I am not asking whether this is good or bad code, just whether it's possible and how it would be done. I wouldn't normally use it since I could just add the increment logic to every method manually and replace all direct instance variable accessing with methods (even things like is_a? and other things inherited from Object)
a pretty hardcore-version would be to use set_trace_func: http://apidock.com/ruby/Kernel/set_trace_func
this allows you to subscribe to all the ruby events fired throughout your program, which can be a ton of calls...
i don't think that there is a build-in hook for registering to arbitrary method-calls. you could implement something with method-missing, method-chaining or delegation, but that would depend on your requirments.
If you don't need everything to be standalone, a suggestion would just be to extend ActiveModel::Callbacks. Simply extend the class and you'll have all of the functionality of a before_filter without requiring all of the other Rails stuff.
Here is a workaround according to your description. Basically it will incremental #access_times for each of the instance method, and the method also does what it does before.
class Test
attr_accessor :access_times
def initialize
#access_times = 0
end
def get_two
2
end
end
class Test
##im = instance_methods
##im.each do |m|
class_eval <<-END
alias temporary #{m}
END
define_method(m) do |*args, &block|
#access_times += 1
temporary(*args, &block)
end
end
undef :temporary
end
Test.new.get_two # => #access_times += 1 and original get_two is called: 2
While this piece of code doesn't work as expected, I'll have a look at it later. Thanks.

Ruby forwarding method calls

I have an instance of a master class which generates instances of a subclass, these subclasses need to forward some method calls back to the master instance.
At the moment I have code looking something like this, but it feels like I should be able to do the same thing more efficiently (maybe with method_missing?)
class Master
def initalize(mynum)
#mynum = mynum
end
def one_thing(subinstance)
"One thing with #{subinstance.var} from #{#mynum}"
end
def four_things(subinstance)
"Four things with #{subinstance.var} from #{#mynum}"
end
def many_things(times,subinstance)
"#{times} things with #{subinstance.var} from #{#mynum}"
end
def make_a_sub(uniqueness)
Subthing.new(uniqueness,self)
end
class Subthing
def initialize(uniqueness,master)
#u = uniqueness
#master = master
end
# Here I'm forwarding method calls
def one_thing
master.one_thing(self)
end
def four_things
master.four_things(self)
end
def many_things(times)
master.many_things(times,self)
end
end
end
m = Master.new(42)
s = m.make_a_sub("very")
s.one_thing === m.one_thing(s)
s.many_things(8) === m.many_things(8,s)
I hope you can see what's going on here. I would use method_missing, but I'm not sure how to cope with the possibility of some calls having arguments and some not (I can't really rearrange the order of the arguments to the Master methods either)
Thanks for reading!
Support for the Delegation Pattern
Delegate
Does the Delegate help here? It allows you to delegate methods to a second class
This library provides three different ways to delegate method calls to an object. The easiest to use is SimpleDelegator. Pass an object to the constructor and all methods supported by the object will be delegated. This object can be changed later.
Going a step further, the top level DelegateClass method allows you to easily setup delegation through class inheritance. This is considerably more flexible and thus probably the most common use for this library.
Finally, if you need full control over the delegation scheme, you can inherit from the abstract class Delegator and customize as needed. (If you find yourself needing this control, have a look at forwardable, also in the standard library. It may suit your needs better.)
Forwardable
There's also the forwardable library
This library allows you delegate method calls to an object, on a method by method basis. You can use Forwardable to setup this delegation at the class level, or SingleForwardable to handle it at the object level.

Override same Class method in Ruby with Multiple Modules, with need to call super. Do I use Method Alias, or some other clever trick?

Here's the situation:
I have a User model, and two modules for authentication: Oauth and Openid. Both of them override ActiveRecord#save, and have a fair share of implementation logic.
Given that I can tell when the user is trying to login via Oauth vs. Openid, but that both of them have overridden save, how do "finally" override save such that I can conditionally call one of the modules' implementations of it?
Here is the base structure of what I'm describing:
module UsesOauth
def self.included(base)
base.class_eval do
def save
puts "Saving with Oauth!"
end
def save_with_oauth
save
end
end
end
end
module UsesOpenid
def self.included(base)
base.class_eval do
def save
puts "Saving with OpenID!"
end
def save_with_openid
save
end
end
end
end
module Sequencer
def save
if using_oauth?
save_with_oauth
elsif using_openid?
save_with_openid
else
super
end
end
end
class User < ActiveRecord::Base
include UsesOauth
include UsesOpenid
include Sequencer
end
I was thinking about using alias_method like so, but that got too complicated, because I might have 1 or 2 more similar modules. I also tried using those save_with_oauth methods (shown above), which almost works. The only thing that's missing is that I also need to call ActiveRecord::Base#save (the super method), so something like this:
def save_with_oauth
# do this and that
super.save
# the rest
end
But I'm not allowed to do that in ruby.
Any ideas for a clever solution to this?
Is that what alias_method_chain would do? I've avoided that because people seemed to say it was a bad idea.
(Finding things as I go):
Alias Method Chain the Ruby Way
Yes alias method chain would help you in this situation.
But consider using delegate pattern. Original save method would trigger a callback on special delegate object (which can be as well nil) and it would do whatever needs to be done when saving user.
Also there is simliar pattern supported directly by actve record called Observer, try to read somethng about it maybe that's a good solution too.
I'm not saying this chaining methods is wrong, but there are cleaner ways to achieve what you want.

Resources