Rspec for the following method - ruby-on-rails

Can anyone please help me in writing rspec for the following method The write_entry_to_xml method write xml nodes.
The entry is an object which looks like
entry = Sitemap::Entry.new("http://www.example.com", 'monthly', 0.8, "2011-11-23 13:56:42 UTC")
def write_entry_to_xml(entry)
node = Nokogiri::XML::Node.new("url", #xml_document)
node["loc"] = entry.loc
node["lastmod"] = entry.lastmod.to_s
node["changefreq"] = entry.changefreq.to_s
node["priority"] = entry.priority.to_s
node.to_xml
end
Thanks

First off you should know what the expected output of your method should be. We can't tell you what your code is supposed to be doing. Once you have that, your spec doesn't have to be anything more than
describe SomeClass do
describe('write_entry_to_xml') do
let(:entry) { Sitemap::Entry.new("http://www.example.com", 'monthly', 0.8, "2011-11-23 13:56:42 UTC")}
it 'should return a properly formatted xml fragment' do
SomeClass.write_entry_to_xml(entry).should == "<url>...</url>"
end
end
end
Here I've assumed that this method is a class method on SomeClass - change to match what you've done, you should also obviously replace "..." with the desired output

There are various ways to get some pre-defined data that you would use as input in ur spec.
1.
before :each do
#input = input
end
Now #input will be accessible to the specs as this block will be run before every spec.
2. You could define the inputs inside the spec as a local variable and then access it. If you require to access the same variable at many places then you can use the above method.
You could get some XML via using a library like Builder.
builder = Builder::XmlMarkup.new
xml = builder.url do | b|
b.loc = "http://www.experteer.de/jobboerse/deutschland/jobs/mannheim"
b.changefreq =0.8
b.priority = "monthly"
b.lastmod = "2011-11-23 13:56:42 UTC"
end
this code should give you the XML object. Do let me know if you need further help.

Related

Write rspec for method inside method

def some_helper(exam)
x = 1
y = 2
if condition1
x = 3
y = 4
end
if condition2
x = 5
y = 6
end
return_something_base_on_x_y(x,y)
end
def return_something_base_on_x_y(x,y)
return "#{1}/#{2}" if x==1, y==2
return "#{3}/#{4}" if x==3, y==4
return "#{5}/#{6}" if x==5, y==6
end
i will call in view like this
some_helper(exam) # exam is an object
How can i write rspec for some_helper ? Can i write something like bellow. Only test the argument pass to method
describe "#some_helper" do
let(:exam) { Exam.create exam_params }
context "condition 1" do
it do
expect "some_helper" already call return_something_base_on_x_y with arguments(1,2) inside them
expect "some_helper" already call return_something_base_on_x_y with arguments(3,4) inside them
expect "some_helper" already call return_something_base_on_x_y with arguments(5,6) inside them
end
end
end
Can i avoid to write like
expect(some_helper(exam)).to eq "123" # and 456.
Because if condition is more complexity, i have to get a list of return_something_base_on_x_y result.
You can set expectations on a method before it is called by using a double:
it "sets an expectation that a method should be called"
obj = double('obj')
expect(obj).to recive(:foo).with('bar')
obj.foo('bar')
end
The example is failed if obj.bar is not called.
You can set expectations on an object after the call is done by using spies:
obj = spy('obj')
obj.foo('bar')
expect(obj).to have_recived(:foo).with('bar')
This allows you to arrange your tests after the act, arrange, assert pattern (or given, then, when in BDD terms).
Can i avoid to write like
expect(some_helper(exam)).to eq "123" # and 456.
Yes, but it might actually degrade your tests. Stubbing can mask bugs and makes your code more about testing the implementation (how the code does its job) then the behavior (the result).
Stubbing is most suitible when the object you're testing touches an application boundry or is not idempotent (like for example a method that generates random values).

Using .constantize to invoke both a class and method from separate strings

I'm trying to call a class and method which are both stored as strings in the DB.
I would be looking to create an object from these 2 strings:
class_name = 'Some::Class'
method_name = 'a_method'
params = {foo: 'bar'}
class_name.new(params).method_name
I have used constantize before for a similar approach, but I'm not sure exactly what the best solution is here?
You can use const_get:
Class.const_get(class_name).new(params).public_send(method_name)
class_name.constantize.new(params).send(method_name) should work?
If you want to send args with the method, you can use, for example, .send(method_name, params)
If you're getting the data from external sources, I'm not sure how secure constantize is, but it works smoothly and is nicely readable. You can also use classify if the format of the class name could vary.
You tagged your question with ruby-on-rails, therefore I suggest using safe_constantize (available in Rails only) and public_send (plain Ruby):
class_name = 'Some::Class'
method_name = 'a_method'
params = {foo: 'bar'}
class_name.safe_constantize.new(params).public_send(method_name)
If the use of Kernel#eval is safe you could do the following.
module Some
class Klass
def initialize(params)
#params = params
end
def a_method
puts "keys = #{#params.keys}"
end
end
end
class_name = 'Some::Klass'
params = { foo: 'bar', oof: 'rab' }
method_name = 'a_method'
str = "%s.new(%s).%s" % [class_name, params, method_name]
#=> "Some::Klass.new({:foo=>\"bar\", :oof=>\"rab\"}).a_method"
eval str
keys = [:foo, :oof]
This approach obviously has wide application.

Don't change string value on insert

I have a Model user with the following method:
def number_with_hyphen
number&.insert(8, "-")
end
When I run it several times in my tests I get the following output:
users(:default).number_with_hyphen
"340909-1234"
(byebug) users(:default).number_with_hyphen
"340909--1234"
(byebug) users(:default).number_with_hyphen
"340909---1234"
(byebug) users(:default).number_with_hyphen
"340909----1234"
It changes the number ?Here are the docs https://apidock.com/ruby/v1_9_3_392/String/insert
When I restructure my method to:
def number_with_hyphen
"#{number}".insert(8, "-") if number
end
If works like expected. The output stays the same!
How would you structure the code, how would you perform the insert?
which method should I use instead. Thanks
If you're using the insert method, which in the documentation explicitly states "modifies str", then you will need to avoid doing this twice, rendering it idempotent, or use another method that doesn't mangle data.
One way is a simple regular expression to extract the components you're interested in, ignoring any dash already present:
def number_with_hyphen
if (m = number.match(/\A(\d{8})\-?(\d+)\z/))
[ m[1], m[2] ].join('-')
else
number
end
end
That ends up being really safe. If modified to accept an argument, you can test this:
number = '123456781234'
number_with_hyphen(number)
# => "12345678-1234"
number
# => "123456781234"
number_with_hyphen(number_with_hyphen(number))
# => "12345678-1234"
number_with_hyphen('1234')
# => "1234"
Calling it twice doesn't mangle anything, and any non-conforming data is sent through as-is.
Do a clone of the string:
"#{number}".clone.insert(8, '-')

how to create dynamic variables in rails function?

I have this code that's working:
case index
when "Books"
#reading_img = res.items.first.get_hash('MediumImage')["URL"] # don't show an image
#reading_link = create_amz_url(search, asin)
tempy = #nowreading.content.match(/#nowreading.*/).to_s.gsub("#nowreading",'') # strips away the #nowreading tag
#nowreading.content = tempy.match(/#{search}.*/).to_s.gsub("#{search}", #reading_link)
# replaces the book title (passed in as variable 'search') with the URL'ed title
when "Music"
#listening_img = res.items.first.get_hash('MediumImage')["URL"] # don't show an image
#listening_link = create_amz_url(search, asin)
tempy = #nowlistening.content.match(/#nowlistening.*/).to_s.gsub("#nowlistening",'') # strips away the #nowreading tag
#nowlistening.content = tempy.match(/#{search}.*/).to_s.gsub("#{search}", #listening_link)
# replaces the song title (passed in as variable 'search') with the URL'ed title
end
I need to repeat this for many, many categories. I tried something like this to DRY the code but it didn't work:
def get_img_and_links(act, res, search, asin)
'#'+act+'ing_img' = res.items.first.get_hash('MediumImage')["URL"] # don't show an image
'#'+act+'ing_link' = create_amz_url(search, asin)
tempy = '#now'+act+'ing'.content.match(/#now"#{act}"ing.*/).to_s.gsub("#now#{act}ing",'') # strips away the #nowreading tag
'#now'+act+'ing'.content = tempy.match(/#{search}.*/).to_s.gsub("#{search}", '#'+act+'ing_link')
# replaces the book title (passed in as variable 'search') with the URL'ed title
end
Essentially, I was trying to create a function that took an "act" (e.g., "read", "listen", etc) and have the variables within that function be dynamic. Can this be accomplished? If so, how?
Look up instance_variable_set here: http://ruby-doc.org/core/classes/Object.html. It's what you need to dynamically create these variables.
instance_variable_set "##{act}ing_img".to_sym, res.items.first.get_hash('MediumImage')["URL"]
And so on...
Good looking out trying to dry up your code. I would definitely use some hashes there instead of instance variables. Then you can use the key as the action. Just a thought.
IMPO, I think you should use more generic variables. Although the creating variables are supported by ruby, it will make your code hard to read
my suggestion is having some generic names like
#img (instead of reading_img, listing_img etc..)
#link (instead of reading_link, listing_link etc..)
and something like #content, because as your login at a given time only one will be selected and this wouldn't be a problem
Again, this is what i understood from the code you posted and correct me if I'm wrong
cheers
sameera
you should do something like this:
def setup
#reading_img = get_img(whatever)
#reading_link = get_link(whatever)
#listening_img = get_img(whatever)
#listening_link = get_link(whatever)
end
def get_img(whatever)
...do stuff and return stuff...
end
def get_link(whatever)
...do stuff and return stuff...
end

Ruby returns TypeError: can't convert Object into String

I'm starting to work with Ruby and I have the following module:
module RadioreportHelper
#totalsum = 0
#radio
#daysAndTotals
def load(service, transactionsPerDay)
puts "#{service} : #{transactionsPerDay}"
#radio = service
#daysAndTotals = transactionsPerDay
transactionsPerDay.each{|day, total|
#totalsum += total
}
end
attr_reader :radio, :daysAndTotals, :totalsum
end
I'm running the following unit test case:
class RadioreportHelperTest < ActionView::TestCase
fixtures :services
def test_should_return_populated_radio_module
service = Service.find_by_name("Mix")
transactionsPerDay = {1=>20, 2=>30, 4=>40, 5=>50}
radioReport = RadioreportHelper.load(service, transactionsPerDay)
assert_not_nil(radioReport)
assert_equal("Mix", radioReport.radio.name)
end
end
But I always get the following error:
TypeError: can't convert Service into String
I want the service object to be in the module RadioreportHelper and stored it in the #radio variable not the string.
Thanks, for the all the help guys!
Try adding a to_s method to your Service model.
def to_s
service # or some other method in the model that returns a string
end
It is not necessary to call to_s from inside an interpolated expression, i.e. "#{service}" will return the result of service.to_s.
EDIT
Never mind all of this. Your RadioreportHelper.load method is not being reached -- instead you are getting load from ActiveSupport::Dependencies::Loadable. Try renaming the load method to something else.
(I hate name collisions.)
You should definitely try this:
puts "#{service.to_s} : #{transactionsPerDay}"
Although I am not sure how interpolation for hashes is handled in strings either, so you may need to use
puts "#{service.to_s} : #{transactionsPerDay.to_s}"

Resources