Ruby Metaprogramming - ruby

I am having a method which returns the price of a given symbol and i am writing a test for that method.
This is my test
def setup
#asset = NetAssetValue.new
end
def test_retrieve_price_for_symbol_YHOO
assert_equal(33.987, #asset.retrieve_price_for_a_symbol('YHOO'))
end
def test_retrive_price_for_YHOO
def self.retrieve_price_for_a_symbol(symbol)
33.77
end
assert_equal(33.97, #asset.retrieve_price_for_a_symbol('YHOO'))
end
This is my method.
def retrieve_price_for_a_symbol(symbol)
symbol_price = { "YHOO" => 33.987, "UPS" => 35.345, "T" => 80.90 }
raise Exception if(symbol_price[symbol].nil?)
symbol_price[symbol]
end
I am trying to mock the retrieve_price_for_a_symbol method by writing same method in test class but when i call it, the call is happening to method in main class not in the test class.
How do I add that method to meta class from test and how do i call it? Please help.

Instead of re-defining the method inside, you need to mock it out.
Replace the method definition inside the test with
#asset.expects(:retrieve_price_for_a_symbol).with('YHOO').returns(33.97)

Assuming you don't really want to mock the method you're testing...
You are currently defining your mock on the instance of the test class. You can add your mock directly to the #asset object:
def test_retrive_price_for_YHOO
def #asset.retrieve_price_for_a_symbol(symbol)
33.77
end
assert_equal(33.97, #asset.retrieve_price_for_a_symbol('YHOO'))
end

Related

Ruby: Calling instance method from class method

I want to call some_instance_method from some_class_method. In the following code, the class method is being called from the instance method.
class Foo
def self.some_class_method
puts self
end
def some_instance_method
self.class.some_class_method
end
end
Does what you want to do really make sense? Which instance should the instance method be called on? If it doesn't matter, you can do this:
class Foo
def self.some_class_method
new.some_instance_method
puts self
end
def some_instance_method
self.class.some_class_method
end
end
This will cause an infinite loop, of course.
To call an instance method you need to have that object already instantiated somewhere, you can't call an instance method without an object.
def self.some_class_method
puts self.first.some_instance_method
end
Something like this should work
You need to pass the instance you want to be the recipient of the call to the class method. So the new code is:
class Foo
def self.some_class_method(instance)
instance.some_other_instance_method
puts self
end
def some_instance_method
self.class.some_class_method(self)
end
end
Notice that the class method is calling 'some_other_instance_method'. If the class method were to call the same method that called it, it would enter an endless loop.

in ruby, is it possible to redefine the execution context of an added method?

For example
class Foo
def bar
end
end
In that code, bar would only be available within any instance of the class.
Is it possible to change the execution context of the method to the Eigenclass
without changing how the method itself is defined so that the method is now available as a singleton without ever needing to call self.new?
Preferably I would like to do it any of the code that doe this to code that is added in via a class that Foo could inherit from.
At the moment what I'm doing amounts to:
class Test
def method_added method
self.define_singleton_method method do
self.new.send method
end
end
end
and for what I need this doesn't work as I'm changing the execution context by calling new.
You can simply do:
class Test
def self.method_added method
module_function method
end
end
class A < Test
def foo
:hello
end
end
A.foo #=> :hello
I worked out how to do it just now >_<.
Here's the code:
class Test
def method_added method
m = self.new.method(method) #get method object
self.define_singleton_method(method) do #create method with same name within the singleton class
m.call #call the block which will now run the code of the added method within the context of the Eigenclass/Singleton
end
end
end
So what it does is it grabs a method object form an instance and then calls that method as a block within the context of the class.
So the first code example becomes:
class Foo < Test
def bar
end
end
and the method bar can now be accessed as
Foo.bar rather then Foo.new.bar which means no instance creation; besides the time it does within method added, but that's fine as it's the only way to get the method object as far as I'm aware.
Which is why it's probably best to create an instance only the once when the class is inherited (within def self.inherited), store it within the class and then just access that instead of calling self.new.

How to call a Method in a Cucumber - Step Definition

I’m a newbie to Cucumber framework. I’m trying to call a Ruby method inside of a step definition. Here is how I define my method in lib/methods.rb
class Test_class
def create_test_scenario()
puts "here!!!"
end
end
This is how I try to call the method inside of a step definition:
And(/^I create scenarios$/) do
Test_class.create_test_scenario
end
I'm getting 'uninitialized constant Test_class (NameError)' when I run the test. Any ideas? Thanks.
You haven't instantiated the Test_class object. For example:
class Test_class
def create_test_scenario
puts "here!!!"
end
end
Test_class.new.create_test_scenario # notice `new` method chained here
#=> here!!!
Errata:
Here's a link to documentation that explains the initialize method and how you can use it to set up object state on initialization.
For class (and module) names, the ruby convention is to use CamelCase. For example, TestClass instead of Test_class
As orde has said, this is down to initialization. To help put the code into context, you would initialize the class object in your step definition as an instance variable (which starts with #). So it would look like this:
And(/^I create scenarios$/) do
#Test_class = Test_class.new
#Test_class.create_test_scenario
end

How to assert certain method is called with Ruby minitest framework?

I want to test whether a function invokes other functions properly with minitest Ruby, but I cannot find a proper assert to test from the doc.
The source code
class SomeClass
def invoke_function(name)
name == "right" ? right () : wrong ()
end
def right
#...
end
def wrong
#...
end
end
The test code:
describe SomeClass do
it "should invoke right function" do
# assert right() is called
end
it "should invoke other function" do
# assert wrong() is called
end
end
Minitest has a special .expect :call for checking if some method is called.
describe SomeClass do
it "should invoke right function" do
mocked_method = MiniTest::Mock.new
mocked_method.expect :call, return_value, []
some_instance = SomeClass.new
some_instance.stub :right, mocked_method do
some_instance.invoke_function("right")
end
mocked_method.verify
end
end
Unfortunately this feature is not documented very well. I found about it from here: https://github.com/seattlerb/minitest/issues/216
With minitest you use expect method to set the expectation for a method to be called on a mock object like so
obj = MiniTest::Mock.new
obj.expect :right
If you want to set expectation with parameters and return values then:
obj.expect :right, return_value, parameters
And for the concrete object like so:
obj = SomeClass.new
assert_send([obj, :right, *parameters])
According to the given example, there is no other delegate class, and you want to make sure the method is called properly from the same class. Then below code snippet should work:
class SomeTest < Minitest::Test
def setup
#obj = SomeClass.new
end
def test_right_method_is_called
#obj.stub :right, true do
#obj.stub :wrong, false do
assert(#obj.invoke_function('right'))
end
end
end
def test_wrong_method_is_called
#obj.stub :right, false do
#obj.stub :wrong, true do
assert(#obj.invoke_function('other'))
end
end
end
end
The idea is to stub [method_expect_to_be_called] by returning a simple true value, and in the stub block assert it's indeed being called and returning the true value. To stub the other unexpected method is just to make sure that it's not being called.
Note: assert_send won't work correctly. Please refer to official doc.
In fact, below statement will pass, but doesn't mean it's working as expected:
assert_send([obj, :invoke_function, 'right'])
# it's just calling invoke_function method, but not verifying any method is being called
To stub and assert method calls, you use MiniTest::Mock. There are 2 ways to use this:
stub an object's method to return a mock object, which has a stubbed method
stub an object's method to call the mock method
test "return the mock object when calling the stubbed method" do
# the object you want to stub
obj = Book.new
mock = MiniTest::Mock.new
mock.expect :the_method_to_stub, "my cool return value"
obj.stub :method_that_gives_you_a_mock, mock do
x = obj.method_that_gives_you_a_mock
assert_equal x.the_method_to_stub, "my cool return value"
end
# assert that the method was called once
mock.verify
end
test "call the mock method when calling the stubbed method" do
# the object you want to stub
obj = Book.new
mock = MiniTest::Mock.new
# use :call to make the mock a callable
mock.expect :call, "my cool return value"
obj.stub :method_that_calls_the_mock, mock do
assert_equal obj.method_that_calls_the_mock, "my cool return value"
end
# assert that the method was called once
mock.verify
end
To use MiniTest::Mock, you may need to add require 'minitest/autorun' to load the MiniTest constants.
Recently I've created a gem for easing this kind of assertions called 'stubberry'.
Here how you can manage the needed behaviour using it.
First you need to answer the questions:
do you have an access to the original object before the test sequence
execution?
is there any indirect way you can sure call happened? i.e. there should be some methods invocations on some other object you have access to.
do you need the method to be actually called or could it be stubbed with the prooper object or callable?
If you have access to the object, and you can stub the method, with your callable:
obj.stub_must( :right, -> { stub_response } ) {
... do something
}
If you have access to the object but you don't want to stub the method and you just want to ensure that method was invoked:
assert_method_called( obj, :right ) {
.... do something with obj
}
If you don't have the access to the object you want to test against.
But you can do indirect check with some other object method invocation, Lets say 'right' method will end with API call execution:
API.stub_must( :get, -> (path, params) {
assert_equal( path, :expected_path )
assert_equal( params, {} )
} ) do
... do something
end
If you can't do an indirect check:
stunt_boudle = Obj.new
stunt_boudle.stub_must( :right, -> () {
#do needed assertions
} ) do
Obj.stub_must(:new, stunt_boudle) do
# do some integration testing
end
end
# OR use assert_method_called the same way
Also there is a cool set of stubbing ActiveRecord object by id, you can use them in this case when you can't have access to the object at the start of the testing actions and its an ActiveRecord object.

Mocking the initialize method on a ruby class?

How can I mock the initialize method on a ruby class?
I'm doing some testing and want to mock out the object that is created from a new call.
I tried to write a few things and none of them seemed to get the mock class to return from the new call. It just keeps returning the normal, expected object.
EDIT:
one attempt -
class MockReminderTimingInfoParser < ReminderTimingInfoParser
def new(blank)
ReminderTimingInfoParserForTest.new
end
end
describe ReminderParser do
parser = ReminderParser.new(MockReminderTimingInfoParser)
it "should parse line into a Reminder" do
parser.parse(" doesnt matter \"message\"").should == Reminder.new('message', ReminderTimingInfo.new([DaysOfWeek.new([:sundays])], [1]))
end
end
class ReminderTimingInfoParserForTest
include TimingInfoParser
def parse_section(section); [DaysOfWeek.new([:sundays]), 1] end
def reminder_times_converter(times); times end
end
class MockReminderTimingInfoParser < ReminderTimingInfoParser
def new(blank)
ReminderTimingInfoParserForTest.new
end
end
Here, you are defining a method called new for all instances of the class MockReminderTimingInfoParser. In your question, you mention that you want to hook into instance creation. However, in Ruby, instance creation is not done by instance methods. Obviously, this cannot work, since in order to call an instance method, you'd need to have an instance first!
Instead, instances are created by calling a factory method (commonly called new) on the class.
In other words, in order to create an instance of MockReminderTimingInfoParser, you would call MockReminderTimingInfoParser.new, but you have defined a method MockReminderTimingInfoParser#new. In order to call the method you have defined, you would have to call MockReminderTimingInfoParser.new.new.
You need to define a method on MockReminderTimingInfoParser's singleton class. There's several ways to do that. One way would be just mimicking the way you would call the method:
def MockReminderTimingInfoParser.new(blank)
ReminderTimingInfoParserForTest.new
end
Another would be opening up MockReminderTimingInfoParser's singleton class:
class << MockReminderTimingInfoParser
def new(blank)
ReminderTimingInfoParserForTest.new
end
end
However, in both of these cases, MockReminderTimingInfoParser obviously has to exist first. Given that you need to define the class anyway, here's the most idiomatic way of defining methods on a class's (or module's) singleton class:
class MockReminderTimingInfoParser < ReminderTimingInfoParser
def self.new(blank)
ReminderTimingInfoParserForTest.new
end
end
Could you inherit the class and then supply your own initialize?

Resources