RSpec - trying to stub a method that returns its own argument - ruby

I have a method which I'm trying to stub out in my unit test. The real method gets called with one argument (a string) and then sends out a text message. I need to stub out the method but return the string that gets passed in as an argument.
The code I have in my RSpec test is this:
allow(taxi_driver).to receive(:send_text).with(:string).and_return(string)
This returns:
NameError: undefined local variable or method 'string'
If I change the return argument to :string, I get the following error:
Please stub a default value first if message might be received with other args as well
I've tried googling and checking the relishapp.com site, but can't find the answer to something which appears quite simple and straightforward.

You can pass a block:
allow(taxi_driver).to receive(:send_text).with(kind_of(String)){|string| string }
expect(taxi_driver.send_text("123")).to eq("123")

My method is being called like this: send_text("the time now is #{Time.now}"). The string varies according to the time, thats why I need the mock to return the varying string. Perhaps its not within the scope of a mock to do this?
In such a case, I usually use Timecop gem in order to freeze system time. Here is a sample use case:
describe "#send_text" do
let(:taxi_driver) { TaxiDriver.new }
before do
Timecop.freeze(Time.local(2016, 1, 30, 12, 0, 0))
end
after do
Timecop.return
end
example do
expect(taxi_driver.send_text("the time now is #{Time.now}")).to eq \
"the time now is 2016-01-30 12:00:00 +0900"
end
end

Related

How do i fix an implicit conversion for a ruby json code

I'm trying to save a json file with already inputed data with the code snippet below:
def read_rentals
return [] unless File.exist?('rentals.json')
rentals_json = JSON.parse(File.read('rentals.json'))
rentals_json.map do |rental|
Rental.new(rental['date'], #person[rental['person_index']], #books[rental['book_index']])
end
end
but I get the error message:
block in read_rentals': no implicit conversion from nil to integer (TypeError)
it stopped my script from running. robocop Rental.new line above as the error case.
The JWT.encode method expects a hash of claims. You should simply get the first value of the array and put that in as a parameter. There is no other way around. This is simply the current state of the ruby-jwt implementation.
I found the solution:
in my rentals.json, my person_index was set as null. i changed it to an integer, and the script was working
[
{
"date": "04/23",
"book_index": 0,
"person_index": 0
}
]

Rspec expect with object validate values

I am doing tests and I want to assert that an object that goes to a dependency contains the values I am expecing. For this I have the next piece of code:
foo = Foo.new(1, 10, 0)
dependency = double
expect(dependency).to receive(:function).with(foo)
But this fails, obviously, as in the code I do a Foo.new ... so this object is not the same as the one in the logic. It makes sense, and I get an error like the next:
#<Double (anonymous)> received :function with unexpected arguments
expected: (#<Foo:0x00000000001 #value_1=1, #value_2=10, #value_3=0>)
got: (#<Foo:0x00000000002 #value_1=1, #value_2=10, #value_3=0>)
But the values on the object are the same.
Is there any way of validating only the values and not the object itself?
I can do the following, and it works, but this scenario has just three properties. On a bigger object, this will be a bit of a mess.
expect(dependency).to receive(:function).with(having_attributes(value_1: 1, value_2: 10, value_3: 0)
can this validation be done out of the box?
Thanks.
EDIT1: This is the code that I am trying to test
foo = Foo.new(value_1, value_2, value_3)
#dependency.function(foo)

How to make Ruby Mocha mock only check about one parameter

I want to mock this function:
def self.set_segment_info(segment_info, history_record)
history_record.segment_info = segment_info
end
In my test, I want a mock that only confirms that I called set_segment_info with an expected value. I don't care about what I pass in for history_record.
How would I do this? I tried
SegmentHistoryRecord.expects(:set_segment_info).with(:segment_info => expected_segment_info, :history_record => anything)
But that doesn't work.
I ran into this today and ended up doing something like:
SegmentHistoryRecord.expects(:set_segment_info).with(
expected_segment_info,
anything
)
I find it more readable that the do version and it helped me avoid a rubocop issue with too many parameters.
Here's an implementation where, if your function takes a lot of parameters, it's more convenient to specify a value for just the one you care about, instead of for all of them:
expected_segment_info = # ...
SegmentHistoryRecord.expects(:set_segment_info).with() { |actual_parameters| actual_parameters[:segment_info] == expected_segment_info }
(Where, as in the original question, set_segment_info is the function being mocked, and segment_info is the parameter whose value you want to match. Note that the history_record parameter -- and any others that might be present -- don't need to be included.)
SegmentHistoryRecord.expects(:set_segment_info).with() do |param1, param2|
# change below to your verification for :segment_info
# and leave param2 doing nothing, the expectation will ignore param2
param1 == expected_segment_info
end

Clear input field: undefined method `clear' for #<Watir::Input:XYZ> (NoMethodError)

I am not sure why I can not clear my input field.
PageObject:
element(:test_input_field) { |b| b.input(class: "search-field") }
def set_search_value(search_entry)
test_input_field.when_present.clear
test_input_field.when_present.set(search_entry)
end
Step_file:
page.set_search_value(search_entry)
Output:
undefined method `clear' for #'<'Watir::Input:0x00000003980d20'>' (NoMethodError)
The clear (and set) method are not defined for generic input elements - ie Watir::Input. They are only defined for the specific input types - text field, checkbox, etc.
To make the code work, you would need to convert the input into the more specific type, which is likely a text field. You can do this using the to_subtype method:
test_input_field.when_present.to_subtype.clear
test_input_field.when_present.to_subtype.set(search_entry)
As #SaurabhGaur mentions, set already starts by clearing the existing value, so you could just do:
test_input_field.when_present.to_subtype.set(search_entry)
Unless the input type changes, it would make more sense to define the element as a text_field so you do not need to convert it. It might depend on which page object library you are using, but I would expect you could do:
element(:test_input_field) { |b| b.text_field(class: "search-field") }
def set_search_value(search_entry)
test_input_field.when_present.clear
test_input_field.when_present.set(search_entry)
end

How to pass special parameters in a function in Ruby Selenium Webdriver

I'm using Selenium Webdriver Ruby to create a function in order to wait for an element displayed within a Time that I set in this function
My function will have two parameters (one Optional parameter & one parameter is locator name of an object)
So, I have my function with two parameters like below:
def wait_for_element_displayed(locator, options={})
if options[:time]
settime = options[:time]
else
settime= 60
end
#driver.find_element(locator).click
end
So, I will call this function like: wait_for_element_displayed(:id=>"unique1", :time => "20")
However, I got the error when trying to calling this function with this way, the error is: "TypeError: can't convert String into an exact number".
Anybody please help me how to pass two parameters like my scenario to a function? Any help appreciated.
A few suggestions:
You can use hash#fetch to set a default value if the key you are looking up is not present:
time = options.fetch(:time) { 60 }
Ruby will have a hard time parsing your input when both arguments are hashes and you don't differentiate between them. Also, your error makes me think you should provide 20 as an integer instead of a string. Try calling the method like this:
wait_for_element_displayed({:id => "unique1"}, { :time => 20 })
Lastly, I don't know much about Selenium, so maybe someone can fill me in on the last point. Is setting a settime variable sufficient to configure timeouts?

Resources