I am trying to test if a dynamic method call was performed using RSpec. Having a bit of trouble.
I have code that looks like :
def self.parse_file
method_name = "parse_#{get_file_type}"
send method_name
end
def self.parse_gz
....
end
Assuming that get_file_type returns "gz", I want to test that parse_gz is called from the parse_file instance method.
Initially, I was thinking something like below, but I think I'm going about it wrong...
Class.should_receive(:parse_gz).with(Class.parse_file)
...that doesn't work
Any help is greatly appreciated...
Your version does not work, because you never call parse_gzwith an argument.Try this:
Class.should_receive(:parse_file)
or
Class.should_receive(:send).with('parse_gz')
Furthermore I would suggest to use the new allow(thing).to receive(method) syntax instead of the old thing.should_receive(method) syntax.
Related
So, I'm currently learning about metaprogramming in Ruby and I want to fully understand what is happening behind the scenes.
I followed a tutorial where I included some of the methods in my own small project, an importer for CSV files and I have difficulties to wrap my hand around one of the methods used.
I know that the define_method method in Ruby exists to create methods "on the fly", which is great. Now, in the tutorial the method initialize to instantiate an object from a class is defined with this method, so basically it looks like this:
class Foo
def self.define_initialize(attributes)
define_method(:initialize) do |*args|
attributes.zip(args) do |attribute, value|
instance_variable_set("##{attribute}", value)
end
end
end
end
Next, in an initializer of the other class first this method is called with Foo.define_initialize(attributes), where attributes are the header row from the CSV file like ["attr_1", "attr_2", ...], so the *args are not provided yet.
Then in the next step a loop loops over the the data:
#foos = data[1..-1].map do |d|
Foo.new(*d)
end
So here the *d get passed as the *args to the initialize method respectively to the block.
So, is it right that when Foo.define_initialize gets called, the method is just "built" for later calls to the class?
So I theoretically get a class which now has this method like:
def initialize(*args)
... do stuff
end
Because otherwise, it had to throw an exception like "missing arguments" or something - so, in other words, it just defines the method like the name implies.
I hope that I made my question clear enough, cause as a Rails developer coming from the "Rails magic" I would really like to understand what is happening behind the scenes in some cases :).
Thanks for any helpful reply!
Short answer, yes, long answer:
First, let's start explaining in a really (REALLY) simple way, how metaprogramming works on Ruby. In Ruby, the definition of anything is never close, that means that you can add, update, or delete the behavior of anything (really, almost anything) at any moment. So, if you want to add a method to Object class, you are allowed, same for delete or update.
In your example, you are doing nothing more than update or create the initialize method of a given class. Note that initialize is not mandatory, because ruby builds a default "blank" one for you if you didn't create one. You may think, "what happens if the initialize method already exist?" and the answer is "nothing". I mean, ruby is going to rewrite the initialize method again, and new Foo.new calls are going to call the new initialize.
I am building an application with Dashing/Smashing right now, and I am using rspec to test my code. However, I cannot figure out how to check that send_event is called. I have tried
expect(Sinatra::Application).to receive(:send_event).twice
and
expect(Dashing).to receive(:send_event).twice,
but neither have worked. I am not sure what object is supposed to receive the call to send_event since it lies inside Dashing in app.rb. There is also this issue, talking about the same thing, unanswered on the Dashing GitHub.
Any advice on how to do this would be much appreciated. Thank you!
Update:
I still have not figured out how to do this, but I have discovered that this works:
let(:dummy_class) { Class.new { include Dashing } }
context 'something' do
it 'does something' do
expect(dummy_class).to receive(:send_event).once
dummy_class.send('send_event', 'test', current: 'test')
end
end
However, if I use the method I want to call that contains send_event as opposed to doing dummy_class.send(...), then it does not recognize that the method was called. It must have to do with my test not using the dummy class. I don't know if there is any way to get around this and make it use the dummy class.
I figured it out!
Do not call send_event directly within the job. Call it within some other class, perhaps called EventSender. Then, to test that send_event is called, treat it as though it is an instance method of that class instead of a method of a module. Your code might look like this, for example:
describe 'something' do
context 'something' do
it 'does something' do
happy_es = EventSender.new(...)
expect(happy_es).to receive(:send_event).with(...)
happy_es.method_that_calls_sendevent
end
end
end
Hope this helps someone who is struggling with the same thing. :)
I have this code:
class B
def self.definer(name, *args, &block)
define_method(name) { self.instance_exec(*args, &block) }
end
end
and when I try to use it, I get this error:
B.definer(:tst) { super }
# => :tst
B.new.tst
# => TypeError: self has wrong type to call super in this context: B (expected #<Class:#<Object:0x007fd3008123f8>>)
I understand that super has a special meaning, and works little different from calling a method. Can someone explain why and what is happening? It would also be great if someone suggests a solution for this.
I don't get the same error message as you did, but get an error anyway. super must be used within a method definition. But you are not using it in a method definition. That raises an error.
Regarding the solution, I cannot give you one since it is not clear at all what you are trying to do.
You definitely don't want instance_exec there.
If you didn't have the *args involved, I'd say you just wanted this:
def self.definer(name, &block)
define_method(name, &block)
end
But then your new definer method would do the exact same thing that define_method does in the first place, so there's be no reason to create it, instead of just using define_method in the first place.
What are you actually trying to do? Explain what you want to do, and maybe someone can help you.
But I think the instance_exec in your existing implementation isn't what you want -- it is immediately executing the block upon definer call, when calling define_method -- I think you want the block executed when the method you are defining is being called instead? But I'm not really sure, it depends on what you're trying to do, which is unclear. super doesn't really make any sense within an instance_exec -- super to what method did you think you'd be calling?
Can I have an object reinitialize itself before a method is executed? I'm using Ruby and selenium to test a web app and I am trying to improve my page objects. for example
class Foo
def initialize
#stuff happens here
end
def NewMethod
self.initialize
#What happens here is what I really want to happen
end
end
Is this a good or bad idea? Or is there a better way to do this?
it's always possible to change the data contained in your object. you could ie put all the init-logic into an additional method and then call that from your custom methods.
in general, what you are trying to do does not sound like a good idea...
A remark in advance: methods are written in lower case. Please replace NewMethod with newmethod.
If you try Foo.newmethod you get an error.
What do you want to do? Do you want to define different possibilities to create an object?
What you could do:
class Foo
def initialize
end
def self.newmethod
me = self.new
#Some actions ...
me #Return the (modified?) object
end
end
p Foo.newmethod #->#<Foo:0xb77d58>
Time is using something like this. There is Time.new, Time.gm, Time.local...
I'm adding unit tests to a large batch of code and am looking for a way to insert fake methods in for testing purposes. The problem is that, as far as I know in Ruby, to pass a method in one must use ClassName.method(:method_name), and then refactor the method I'm testing to use boo.call() instead of just boo(). Is there an easier way to do this than refactoring everything to use .method and .call?
Why not just pass lambdas? I mean, lambda is just an anonymous method/function as you know it from other languages, so it should work fine. Eg:
fake_method = lambda { |n| "do something with n" }
def other_method(fm)
#...
fm.call
#...
end
other_method(fake_method)
You still need to call .call though