Rspec: Getting error while testing private method - ruby-on-rails

Getting error while testing a private method. Please suggest how to test private method is called from a public method.
Public
def public_method
private_method
end
Private
def private_method
tries = 0
begin
raise Product::StaleObjectError.new("Product is changed while you were editing") if stale_object?
// Do some work
raise Exception.new("Total amount used is greater than approved") if total_approved < 0
// Save Product
rescue Product::StaleObjectError => e
if tries < MAX_RETRIES
tries += 1
sleep(1 + tries)
reload
retry
else
raise Product::StaleObjectError("Product is changed while you were editing")
end
end
attributes
end
Test Case:
before(:each) do
#prod_v1 = Product.new
end
it 'test private method called' do
expect_any_instance_of {Product}.to receive(:private_method)
#prod_v1.public_method
end
I am getting following error for test case
Failure/Error: expect_any_instance_of {Product}.to receive(:)
ArgumentError:
wrong number of arguments (0 for 1)

According to the documentation for expect_any_instance_of, this will receive the class as a method argument, so you should use parenthesis instead of curly braces:
it 'test private method called' do
expect_any_instance_of(Product).to receive(:private_method)
...
end

Related

Random results when trying to set an attachment with ActiveStorage

I have the following in my test:
...
let(:image) { Rack::Test::UploadedFile.new('spec/support/assets/test.png', 'image/png') }
subject { described_class.new(gold_bar_order).set_bar_custom_image(image) }
it do
subject
expect(gold_bar_order.reload.bar_custom_image.present?).to be_truthy
end
And here is the class from the method I'm testing:
class GoldBarOrderCustomizationUpdater
def initialize(gold_bar_order)
#gold_bar_order = gold_bar_order
end
def set_bar_custom_image(bar_custom_image)
update_values do
#gold_bar_order.bar_custom_image.attach(bar_custom_image)
#gold_bar_order.save!
end
end
...
private
def update_values
ActiveRecord::Base.transaction do
yield
calculator = MasterOrderCalculator.new(#gold_bar_order.master_order)
calculator.recalculate_sub_orders!
calculator.save_values!
end
end
end
What is happening is that I'm receiving random results for the described test: if I run it a given number of times, it fails or passes without particular order.
I couldn't understand why. Any guesses?

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

How to stub a method having a parameter and raise exception using mocha?

I am trying to stub an instance method of a class having a single parameter. Here is a simplified form of the class.
class GetPost
def call(post_id)
#some meaningful stuffs
end
end
class GetPreviewTemplate
def call(post_id)
begin
GetPost.new().call(post_id)
rescue => e
Post.find_by_ref(post_id).update(failed: true)
raise
end
end
end
And here is how I am trying to stub call method of GetPost to raise an error so that I can verify failing scenario in GetPreviewTemplateTest.
test 'that should NOT get the HTML content of non-existing dummy post from CMS' do
GetPost.any_instance.stubs(:call).with(any_parameters).raises(ArgumentError)
GetPreviewTemplate.new(#post.id).call
assert_not #post.status
end
I am getting following error while running the test
Wordpress::GetPreviewTemplateTest#test_that_should_NOT_get_the_HTML_content_of_non_existing_dummy_post_from_CMS:
ArgumentError: ArgumentError
app/services/wordpress/get_preview_template.rb:19:in `call'
Suggestions, please.

Getting undefined method error in RSpec

I'm using RSpec and FactoryGirl for testing my models and I'm stuck at "highest_priority" method which can't be seen by RSpec for some reason.
Here's the method itself:
models/task.rb
class Task < ActiveRecord::Base
#some stuff
def self.highest_priority
p = Task.order(:priority).last.try(:priority)
p ? p + 1 : 1
end
end
And when I run task_spec.rb
require 'spec_helper'
describe Task do
it "returns highest priority" do
last_task = FactoryGirl.build(:task, priority: "5")
last_task.highest_priority
expect(last_task(:priority)).to eq("6")
end
end
I get the following error:
When I'm calling this method in my controller like this
def create
#task = current_user.tasks.build(task_params)
#task.highest_priority
#task.complete = false
respond_to do |format|
if #task.save
format.js
else
format.js
end
end
end
And the method looks like
def highest_priority
self.maximum(:priority).to_i + 1
end
I'm getting
First of all, you better use ActiveRecord's maximum instead of ordering and then picking one, you'll avoid the instance initialization and get a number directly from the query
Task.maximum(:priority)
this could be put in a class method like this
def self.maximum_priority
Task.maximum(:priority) || 0 # fall back to zero if no maximum exists
end
Then for the second half which is updating the method, i would create an instance method for that, and using the class method
def set_maximum_priority
self.priority = self.class.maximum_priority + 1
self
end
Note that I returned self at the end for chainability
Then your action would become something like this
def create
#task = current_user.tasks.build(task_params).set_maximum_priority
#task.complete = false
...
end
You need to create the method as an instance method of Task model. Like below :
class Task < ActiveRecord::Base
#some stuff
def highest_priority
p = Task.order(:priority).last.try(:priority)
p ? p + 1 : 1
end
end

Ruby, Create a Custom Error Message

Here's the code, but a lot of it is irrelevant:
class BankAccount
def initialize(first_name, last_name)
#first_name = first_name
#last_name = last_name
#balance = 0
end
def public_deposit(amount)
#balance += amount
end
def protected_deposit(amount)
#balance += amount
end
protected :protected_deposit
def private_deposit(amount)
#balance += amount
end
private :private_deposit
def call_private_deposit(amount)
private_deposit(amount)
end
def call_protected_deposit(amount)
protected_deposit(amount)
end
#To show that you can't call a private method with a different instance of the same class.
def private_add_to_different_account(account, amount)
account.private_deposit(amount)
end
#To show that you can call a protected method with a different instance of the same class.
def protected_add_to_different_account(account, amount)
account.protected_deposit(amount)
end
end
I load this code into irb using "load './visibility.rb'" and then create an instance:
an_instance = BankAccount.new("Joe", "Bloggs")
Then, I generate a NoMethodError by typing:
an_instance.protected_deposit(1000)
This returns a NoMethodError. This is intentional. However, what I want to happen is for a custom message to be returned instead of the standard NoMethodError - something like "This is a custom error message."
I've been hacking away at this for hours and I'm at my wits end. I'm a relative beginner, so please bear this in mind.
Thanks.
You can rescue the error:
def call_protected_method
instance = BankAccount.new("Joe", "Bloggs")
instance.protected_deposit(1000)
rescue NoMethodError
puts "You called a protected method"
end
If you want to return a custom message, you can rescue the error and raise your own custom exception. Ruby lets you define your own exception classes. But I can't imagine why you would want to do this. You already have a built in exception to handle this.

Resources