How to test a method call in a constructor with rspec - ruby

i have a constructor like this:
class Foo
def initialize(options)
#options = options
initialize_some_other_stuff
end
end
and want to test the call to initialize_some_other_stuff if a instantiate a new Foo object.
I found this question rspec: How to stub an instance method called by constructor? but the suggested solution to call Foo.any_instance(:initialize_some_other_stuff) does not work in my rspec version (2.5.0).
Can anyone help me to test this constructor call?

In you spec, you could have the following:
class Foo
attr_reader :initializer_called
def initialize_some_other_stuff
#initializer_called = true
end
end
foo = Foo.new
foo.initializer_called.should == true
If the constructor calls the initiaize_some_other_stuff method, foo.initializer_called should be true.

Here you go:
stub_model(Foo).should_receive(:some_method_call).with(optional_argument)

Since the initialize_some_other_stuff method is private to the class, you should not care if it executes or not. That said, if that method performs some expensive operation that you don't want your test waiting for, then it is quite okay to mock that operation.
So, if Foo looked like this:
class Foo
attr_reader :options, :other_stuff
def initialize(options)
#options = options
initialize_some_other_stuff
end
def initialize_some_other_stuff
#other_stuff = Bar.new.long_running_operation
end
end
Then you could mock out the call to Bar#long_running_operation like this:
describe Foo do
subject(:foo) { described_class.new(options) }
let(:options) { 'options' }
let(:bar) { instance_double(Bar, long_running_operation: 42) }
before do
allow(Bar).to receive(:new).and_return(bar)
foo
end
it 'initializes options' do
expect(foo.options).to eq(options)
end
it 'initializes other stuff' do
expect(foo.other_stuff).to eq(bar.long_running_operation)
end
end
Now, you're testing the assignments. But, you're not waiting on the expensive operation to complete.

Related

Is it possible to execute a method whenever the class is required?

I have a class:
class foo
GetActiveWindow = Win32API.new("User32","GetActiveWindow", [], 'L')
#hwnd = GetActiveWindow.call()
def baz
#do stuff with #hwnd
end
end
I think in any usual case the above code would work in the way I need it to. For every instance of the running program, the class will obtain the hwnd of the main window.
However in this case, I'm running the ruby script through an application which is always running such that require 'foo.rb' calls #hwnd = GetActiveWindow.call() once but never again.
Is there a method like the following:
class foo
GetActiveWindow = Win32API.new("User32","GetActiveWindow", [], 'L')
def baz
#do stuff with #hwnd
end
def onrequire()
#hwnd = GetActiveWindow.call()
end
end
I know you can define an initialise() method which is called whenever class.new() is called, however in the case of this application it doesn't really make 'sense' to call the method new, if that makes sense... So I was wondering if I could easily shortcut it.
Assuming you meant Foo and not foo as the latter is an invalid constant name in Ruby.
No, there is no such callback for require in classes. Require just loads and parses a file and there is no direct relation between a class and a file (though it is common to have the Foo class in foo.rb). There could be more than one class in a file, none, or one that does not match the filename.
You can either assign the handle in the initializer, as you already have found out (though I did not understand why you don't like the approach:
def initialize()
#hwnd = GetActiveWindow.call()
end
or add a method which memoizes the handle
def hwnd
#hwnd ||= GetActiveWindow.call()
end
Update after comment of OP:
So all this class is suposed to do is obtaining a handle. And you do not want to instantiate it. Also it does not have any state. Then I suppose you make this a class with class-methods:
class Foo
def self.handle
...
end
end
and call it like this
Foo.handle
Just create handle accessor and use itself whenever you want hwnd.
class Foo
GetActiveWindow = Win32API.new("User32","GetActiveWindow", [], 'L')
def baz
# do stuff with handle
PostMessage(handle, ...)
end
def handle
#hwnd ||= GetActiveWindow.call()
end
end
If you're looking to call the method on require then you can simply move the onrequire call outside of the class:
# foo.rb
class Foo
GetActiveWindow = Win32API.new("User32","GetActiveWindow", [], 'L')
def baz
#do stuff with #hwnd
end
def self.on_require
#hwnd = GetActiveWindow.call()
end
end
Foo.on_require
In this case, the last line is ran on require. You'll obviously have to structure your class / instance methods to accommodate what you're trying to do, but this example will get you there.

rspec stubbing passing the same arguments

Sample code:
class Foo
def initialize(abc)
#abc = abc
#bind = bar
end
def bar
SomeClass.new(#abc)
end
end
Now I want to stub bar using rspec and custom stub:
allow('Foo').to receive(:bar).and_return(FakeBar.new)
The issue is that the FakeBar.new has to be initialize with the same arguments :bar receives. Is it possible to get a copy of params passed to :bar at the time we are stubbing and reuse them in the stub class?
Not sure why you want to do what you're doing (probably there is a simpler way), but for what it's worth:
allow("Foo").to receive(:bar) { |arg1, arg2| FakeBar.new(arg1, arg2) }
RSpec docs, block stub implementation

Easily create an Enumerator

When creating methods that yield, sometimes we want it to return an Enumerator if no block is given. The recommended way is basically return to_enum(:name_of_method, [args]) unless block_given?. However, it's a pain to have to type that for every method that does this. Ruby being ruby, I decided to create a make_enum method, similar to attr_accessor, which does this for me:
class Module # Put this in a mixin, but for the purposes of this experiment, it's in Module
def make_enum *args
args.each do |name|
old_method = instance_method(name)
define_method(name) do |*args, &block|
next to_enum(name, *args) unless block
old_method.bind(self).call(*args, &block)
end
end
end
end
Now I can use it like so:
class Test
def test
yield 1
yield 2
end
make_enum :test
end
t = Test.new
t.test { |n| puts n }
# 1
# 2
t.test.to_a #=> [1, 2]
And it works! But it doesn't work if make_enum is before the method definition.
How can I get this method to work before defining a method, so that the following works? Perhaps I need to make use of method_added?
class Test
make_enum :test
def test
yield 1
yield 2
end
end
I don't know if it's a bad idea for it to be before the method, but my reason for thinking that it would be nice to do that is that it better matches the way we use attr_accessor and the like.
Whereas attr_ methods create instance methods newly, your make_enum modifies an existing method, which is rather similar to protected, private, and public methods. Note that these visibility methods are used either in the form:
protected
def foo; ... end
or
protected def foo; ... end
or
def foo; ... end
protected :foo
The latter two ways are already available with your make_enum. Especially, the second form is already possible (which Stefan also notes in the comment). You can do:
make_enum def test; ... end
If you want to do the first form, you should try to implement that in your make_enum definition.

Ruby + Rspec + OpenStruct weird behavior

So i'm experiencing this weird behavior while testing a ruby class. I'm using rspec 3 to test it by the way.
Class Foo has a method 'fetch_object' which calls the 'find' method from class Bar to retrieve an object and than calls the method 'fail' from the fetched object.
The so called weird behavior happens when i expect to receive the method 'fail' once and receive none but if I change the method name for 'faill' it works like a charm :S
here is the drama:
require 'ostruct'
class Foo
def fetch_object
foobar = Bar.find
foobar.fail
end
end
class Bar
def self.find
OpenStruct.new(name: 'Foo Bar')
end
end
describe Foo do
subject { Foo.new }
let(:foo) { OpenStruct.new() }
before do
expect(Bar).to receive(:find).and_return(foo)
end
it 'fetch object with name' do
expect(foo).to receive(:fail)
subject.fetch_object
end
end
I suspect it's because you are setting an expectation on object, which behaviour depends on method_missing (OpenStruct).
For that reason I wouldn't want it as a mock, I would use regular mock (and spec will pass):
let(:foo) { double('foobar') }
You are testing here, if returned object (result of Bar.find) will receive an expected message, without going into implementation details.
Setting expectations on Dynamic classes like ostruct may lead to strange results. It seems that at some point a Kernel#fail method is invoked, thus, changing a name to faill or any other that is not already "taken" by Kernel will make it work.
Other solution would be monkeypatching OpenStruct to avoid method_missing beeing called:
class OpenStruct
def fail
true
end
end
class Foo
def fetch_object
foobar = Bar.find
foobar.fail
end
end
class Bar
def self.find
OpenStruct.new(name: 'Foo Bar')
end
end
describe Foo do
subject { Foo.new }
let(:foo) { OpenStruct.new }
before do
expect(Bar).to receive(:find).and_return(foo)
end
it 'fetch object with name' do
expect(foo).to receive(:fail)
subject.fetch_object
end
end
But I don't know why would you want to do that ;)
More info: Doubles and dynamic classess

How to properly destroy a class

In Ruby, I have a DAO class, which is extended by a class that makes managing the connections easier, which is extended by a class that represents and manipulates data in a DB, which is further extended by another class. To use an animal metaphor it would look like this:
class Animal
...
end
class Mammal < Animal
...
end
class Feline < Mammal
...
end
class Cat < Feline
...
end
class Lion < Cat
...
end
...
In PHP, there is __destruct method that runs when you destroy/delete a class. And should that class extend another class, you simply add parent::__destruct() to the class's __destruct method like this:
public function __destruct() {
// Clean up code for this class here
...
// Execute clean up code for Parent class
parent::__destruct();
}
I could have a similar method for all the classes except Animal. Since it doesn't extend anything, the parent::__destruct(); line is no longer valid.
However, as I understand it, Ruby doesn't have a method like this for its objects. A finalizer can be set, but I decided to just put in a cleanup method I can call whenever I want to destroy/delete a class. That would take care of anything that needed doing prior to my setting the class to nil.
This raises a new problem though. If the method is always named cleanup and I call lion_instance.cleanup, I assume it calls the Lion#cleanup. How then to get it to call the cleanup in class Cat and then Feline and on down the chain?
Or is this a wrong approach and you have a better idea?
The Ruby idiom for this is to yield to a block which does work, and when the block returns, do cleanup. Ruby's built-in "File.open" does this:
File.open("/tmp/foo") do |file|
file.puts "foo"
end
When the block ends, the file is closed for you, without you having to do anything. This is an excellent idiom. Here's how you might implement something like that:
class Foo
def self.open(*args)
foo = new(*args)
yield foo
foo.close
end
def initialize
# do setup here
end
def close
# do teardown here
end
end
And to use it:
Foo.open do |foo|
# use foo
end
Foo#close will be caused automatically after the end
This will work with subclassing as well. That's because class methods are inherited just as are instance methods. Here's the superclass:
class Superclass
def self.open(*args)
o = new(*args)
yield o
o.close
end
def initialize
# common setup behavior
end
def close
# common cleanup behavior
end
end
and two derived classes:
class Foo < Superclass
def initialize
super
# do subclass specific setup here
end
def close
super
# do subclass specific teardown here
end
end
class Bar < Superclass
def initialize
super
# do subclass specific setup here
end
def close
super
# do subclass specific teardown here
end
end
to use:
Foo.open do |foo|
# use foo
end
Bar.open do |bar|
# use bar
end
If you really need to make sure that cleanup happens no matter what, then use an ensure clause in the class method:
def self.open(*args)
foo = new(*args)
begin
yield foo
ensure
foo.close
end
end
This way, cleanup happens even if there is an exception in the block.
You can use ObjectSpace.define_finalizer
Something like:
class Animal
def initialize
ObjectSpace.define_finalizer(self, proc { # your code })
end
end
Well since no one answered your question about the method moving its way up the inheritance chain...
class Cat
def rawr
puts "rawr"
end
end
class Kitty < Cat
def rawr
puts "meow"
super
end
end
Cat.new.rawr
"Rawr"
Kitty.new.rawr
"rawr"
"meow"
Within a method, you can access the superclass's method of the same name by calling super.

Resources