Neelabh Singh
Neelabh Singh

Reputation: 2678

Rspec: How to test an exception which is getting raised in private method?

In my public method #recalculate, calling the private method1. This method throw exception 'ActiveRecord::StaleObjectError'.

def recalculate
  method_1
  self.save!
end

private
def method_1
    begin
      ####
      ####
      if self.lock_version == Product.find(self.id).lock_version
         Product.where(:id => self.id).update_all(attributes)
      else
         raise ActiveRecord::StaleObjectError.new(self, "test")
      end
    rescue ActiveRecord::StaleObjectError => e
        if tries < 3
           tries += 1
           sleep(1 + tries)
           self.reload
           retry
        else
           raise Exception.new(timeout.inspect)
        end
    end
end

Rspec Test case:

  it 'if car is updated then ActiveRecord::StaleObjectError should be raised' do
    prod_v1 =Product.find(@prod.id)
    prod_v2 = Car.find(@prod.id)
    prod_v1.recalculate
    prod_v1.reload  # will make lock_version of prod_v1 to 1

    prod_v2.recalculate # howvever lock_version of prod_v2 is still 0.

    expect{ prod_v2.send(:method1)}.to raise_error(ActiveRecord::StaleObjectError)

Error:

Failure/Error: expect(prod_v2.send(:method1)).to raise_error(ActiveRecord::StaleObjectError) expected ActiveRecord::StaleObjectError but nothing was raised

Please suggest how to write the unit test case for an exception which is raised in private method. I have used send based on the link:

Note: Exception was raised for in the first time because self.lock_version == Product.find(self.id) was false . And in retry self.lock_version == Product.find(self.id) is true so exception is not capture.

Upvotes: 0

Views: 1006

Answers (1)

Peter Mellett
Peter Mellett

Reputation: 562

Here's a simpler version of what your code is actually doing:

class StaleObjectError < Exception
end

class MyClass
  def initialize
    @tries = 0
  end

  def method_1
    begin
      raise StaleObjectError.new("I'm the more specific exception")
    rescue StaleObjectError => e
      if @tries < 3
        @tries += 1
        sleep(1 + @tries)
        retry
      else
        raise Exception.new("I'm the failure case")
      end
    end
  end

end

myObject = MyClass.new

begin
  myObject.method_1
rescue Exception => e
  # in the error condition, this is always #<Exception: I'm the failure case>
  puts e.inspect
end

Which results in

#<Exception: I'm the failure case>

You won't be able to expect the ActiveRecord::StaleObjectError because you mask it with your rescue else- you've converted the StaleObjectError into an Exception

If you want to preserve the StaleObjectError then you can raise e in your rescue else instead. So to use my example code again:

if @tries < 3
  @tries += 1
  sleep(1 + @tries)
  retry
else
  raise e
end

Which would result in

#<StaleObjectError: I'm the more specific exception>

Then your rspec example should be able to expect the code to raise the correct exception type.

Upvotes: 2

Related Questions