Ngoral
Ngoral

Reputation: 4814

Rspec raise_error do not work with custom errors

I have a custom error class ApiError:

class ApiError < StandardError
  attr_reader :message, :code, :details, :raw_json
  def initialize(json)
    @message  = json["message"]
    @code     = json["code"]
    @raw_json = json

    super json.to_s
  end
end

So I tried to write several specs to test it and no one passed. First one:

it 'raises an error' do
  expect { raise ApiError, "Some error"}.to raise_error ApiError.new("Some error")
end

It failes with:

Failure/Error: expect { raise ApiError, "Some error"}.to raise_error ApiError, "Some error"

       expected ApiError with "Some error", got #<ApiError: Some error> with backtrace:
         # ./spec/models/...:39:in `block (4 levels) in <top (required)>'
         # ./spec/models/...:39:in `block (3 levels) in <top (required)>'

And another option that surprises me a lot:

it 'raises an error 2' do
  expect { raise ApiError, "Some error"}.to raise_error ApiError, "Some error"
end

It fails with:

Failure/Error: expect { raise ApiError, "Some error"}.to raise_error ApiError.new("Some error")

       expected #<ApiError: Some error>, got #<ApiError: Some error> with backtrace:
         # ./spec/models/...:43:in `block (4 levels) in <top (required)>'
         # ./spec/models...:43:in `block (3 levels) in <top (required)>'

So, what? In the message, they seem pretty equal. Can anyone know what's the problem? And, also, what's the difference writing ApiError, "123" and ApiError.new("123") in spec files?

==========Upd==========

Commenting out a string with setting a message attribute in ApiError class in case it does not overwrite a default value didn't help

class ApiError < StandardError
  attr_reader :message, :code, :details, :raw_json
  def initialize(json)
    # @message  = json["message"]
    @code     = json["code"]
    @raw_json = json

    super json.to_s
  end
end

Upvotes: 6

Views: 3269

Answers (3)

ecoologic
ecoologic

Reputation: 10420

Also useful with custom errors is checking properties, which can be achieved with having_attributes like so:

expect { raise "oops" }
  .to raise_error(
    an_instance_of(RuntimeError)
      .and having_attributes(message: "oops")
  )

So you can use any custom attribute like raw_json in your example.

Docs here.

Upvotes: 2

usha
usha

Reputation: 29349

message is an attribute of StandardError. You are overriding it and message is nil in your exception object. Rename your attribute and it should pass

class ApiError < StandardError
  attr_reader :api_error_message, :code, :details, :raw_json
  def initialize(json)
    @api_error_message  = json["message"]
    @code     = json["code"]
    @raw_json = json

    super json.to_s
  end
end

it 'raises an error' do
  expect { raise ApiError, "Some error"}.to raise_error ApiError, "Some error"
end

Update:

If you need to set message to json["message"]

  class ApiError < StandardError
    attr_reader :code, :details, :raw_json
    def initialize(json)
      @code     = json["code"]
      @raw_json = json

      super json["message"]
    end
  end

  it 'raises an error' do
    expect { raise ApiError, {"message" => "Some error"}}.to raise_error(ApiError, "Some error")
  end

Upvotes: 5

Mate Solymosi
Mate Solymosi

Reputation: 5967

Since you are passing a string to ApiError.new, your custom initialize method ends up setting the message of the exception to nil:

ApiError.new("Some error").message
#=> nil

Try testing with a proper hash instead:

let(:error_hash) do
  { "message" => "oops", "code" => 42 }
end

expect { raise ApiError, error_hash }.to raise_error ApiError, "oops"

Upvotes: 0

Related Questions