Reputation: 183
I am new to RSpec. I have a method in my model user_profile.rb
def self.create_from_supplement(device, structure)
xml = Nokogiri.parse(structure.to_s)
user_profile = nil
auth_type = xml.%('auth_supplement/auth_type').inner_html
if 'user' == auth_type
user_details_str = xml.%('auth_supplement/supplement_data/content').inner_html rescue nil
return nil if user_details_str.blank?
user_details_xml = Nokogiri.parse(user_details_str)
user_name = user_details_xml.%('username').inner_html
user_profile = UserProfile.find_or_initialize_by(name: user_name)
if user_profile.save
device.update_attributes(user_profile_id: user_profile.id)
else
raise "User Profile Creation Failed because of #{user_profile.errors.full_messages}"
end
end
return user_profile
end
I am writing a unit test case to test when user_profile.save fails, the test case will expect an exception was raised. But in my user_profiles table I have only one column :name.
How to test the case when user_profile.save fails? The most important problem here is I dont find any way to make this user_profile.save to fail.
Some suggests using RSpec Stubs. How do we do that?
Upvotes: 1
Views: 8108
Reputation: 2572
checkout rspec documents:
https://www.relishapp.com/rspec/rspec-expectations/v/2-11/docs/built-in-matchers/raise-error-matcher
describe ':: create_from_supplement' do
it 'blows up' do
expect { UserProfile.create_from_supplement(*args) }.to raise_error(/User Profile Creation Failed because of/)
end
end
Tracing back you code, here are the places that might cause the error, and following what you can consider.
user_details_str = xml.%('auth_supplement/supplement_data/content').inner_html
Here user_details_str
might be an invalid string format (not nil) because whatever you get from 'auth_supplement/supplement_data/content'
is not a correct format.
user_details_xml = Nokogiri.parse(user_details_str)
Here you need to determine what might cause Nokogiri::parse
to give you an invalid result.
user_name = user_details_xml.%('username').inner_html
Then here, same as above.
user_profile = UserProfile.find_or_initialize_by(name: user_name)
So here, you might have an invalid user_name
due to the previous few lines of code, which violates any validation you might have (e.g. too short, not capitalized, or what not).
More Info
So this can go deeper into your code. It's hard to test because your method is trying to do too much. And this clearly violates abc size (there are too many logical branches, more info here: http://wiki.c2.com/?AbcMetric)
I suggest refactoring some branches of this method into smaller single responsibility methods.
Upvotes: 0
Reputation: 26758
With Rspec expectations you have a special syntax for when you expect an error to be raised.
if you did something like this:
expect(raise NoMethodError).to raise_error(NoMethodError)
that wouldn't work - RSpec would not handle the error and would exit.
However if you use brackets:
expect { raise NoMethodError }.to raise_error(NoMethodError)
that should pass.
If you use brackets ( or a do / end block ) than any errors in the block will be 'captured' and you can check them with the raise_error
matcher.
Upvotes: 6