krishna
krishna

Reputation: 183

RSpec to test raise exception in rails

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

Answers (2)

Edmund Lee
Edmund Lee

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.

  1. 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.

  1. user_details_xml = Nokogiri.parse(user_details_str)

Here you need to determine what might cause Nokogiri::parse to give you an invalid result.

  1. user_name = user_details_xml.%('username').inner_html

Then here, same as above.

  1. 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

max pleaner
max pleaner

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

Related Questions