Basically I am trying to mock object here:
if (object.present? && object.deleted_at.nil?)
My model is
class Relation
include Cequel::Record
...
column :deleted_at, :timestamp
end
When I tried the canonical way:
object = Object.new
object....assign necessary fields
object.deleted_at = nil
object = double
Rspec throws:
Failure/Error: method_name.to_s if ExpectationChain === chains.last unless
chains.last.expectation_fulfilled?
NoMethodError:
undefined method `expectation_fulfilled?' for nil:NilClass
When I tried the cheating way:
something = double
relation = double
expect(something).to receive(:nil).and_return(true)
expect(object).to receive(:present).and_return(true)
expect(object).to receive(:deleted_at).and_return(something)
Now (object.present? && object.deleted_at.nil?) evaluate to false
Huh? What should I do then?
EDIT: Tried the solution in the answer
object = instance_double(Relation, :present? => true, :deleted_at => nil)
I still get:
Failure/Error: method_name.to_s if ExpectationChain === chains.last unless chains.last.expectation_fulfilled?
NoMethodError:
undefined method `expectation_fulfilled?' for nil:NilClass
With the "cheating" way you are doing two things wrong.
1) you are expecting, not allowing
2) you are expecting to receive present when you are calling present? (question mark at the end)
Can you please try:
object = double(Relation)
allow(object).to receive(:present?).and_return(true)
allow(object).to receive(:deleted_at).and_return(nil)
Then:
object.present? => true
object.deleted_at.nil? => true
There is also a shorthand:
object = instance_double(Relation, :present? => true, deleted_at => nil)
I prefer the second solution (instance double). Then, you cannot mock a method that is not implemented by Relation
Related
I want to test a method that update promo_code to used if shopping_process is finalized. Additional it should create a pdf using ShoppingProcessDocumentCreatorFetcher
Here is my method which I want to test
def finalize_shopping_process(form)
if finalize_process == true
shopping_process.campaign_code.update(state: 'used')
document_creator_class = ShoppingProcessDocumentCreatorFetcher.new(shopping_process).call
document_creator_class.new(shopping_process).call
end
Success(form)
end
and the specs:
describe 'campain code' do
subject(:process_update) do
described_class.new(
shopping_process: shopping_process,
form: form,
finalize_process: true,
).call
end
it 'updates state of assigned campain code' do
updated_shopping_process = process_update.value!
expect(updated_shopping_process.campaign_code.state).to eq('used')
end
end
At the end I've got an error
NoMethodError:
undefined method `shopping_element_relations' for nil:NilClass
The weird thing is that if I remove this lines from the tested method:
document_creator_class = ShoppingProcessDocumentCreatorFetcher.new(shopping_process).call
document_creator_class.new(shopping_process).call
Specs will pas. I've no clue where I'm wrong.
Edit:
all error below:
ShoppingProcesses::Update.call campain code updates state of assigned campain code
Failure/Error: return false unless parent_group.shopping_element_relations.hiding.any?
NoMethodError:
undefined method `shopping_element_relations' for nil:NilClass
def finalize_shopping_process(form)
if finalize_process == true
shopping_process.campaign_code.update(state: 'used')
document_creator_class = ShoppingProcessDocumentCreatorFetcher.new(shopping_process).call
document_creator_class.new(shopping_process).call
end
Success(form)
end
In the above function document_creator_class this is initialized to the call method and not the class as intended.
Replace the line
document_creator_class = ShoppingProcessDocumentCreatorFetcher.new(shopping_process).call
with
document_creator_class = ShoppingProcessDocumentCreatorFetcher
and then try.
I hope this resolves your problem.
I'm building a Rails spec test that has a Struct called temp_coverage, like this:
temp_coverage = Struct.new(:paydays) do
def calculate_costs
50
end
end
And in my spec, I call a method using the temp_coverage, but I'm getting an error since the code I am testing is doing the following:
temp_coverage.req_subscriber_election_amount = subscriber_election_amount
And I am getting an error:
NoMethodError: undefined method `req_subscriber_election_amount=' for < struct paydays=12 >
How can I stub out the setting of an attribute on a struct in my spec?
Is something like this you're looking for?
temp_coverage = double('temp_coverage', paydays: nil)
allow(temp_coverage).to receive(:calculate_costs).and_return(50)
allow(temp_coverage).to receive(:req_subscriber_election_amount=) do |argument|
temp_coverage.instance_variable_set(:#req_subscriber_election_amount, argument)
end
# Example:
temp_coverage.req_subscriber_election_amount = 123
puts temp_coverage.instance_variable_get(:#req_subscriber_election_amount)
# => 123
puts temp_coverage.paydays
# => nil
puts temp_coverage.calculate_costs
# => 50
I found a way to do this by using a named Struct. So once I named my Struct:
temp_coverage = Struct.new('CoverageClass', :paydays) do
def calculate_costs
50
end
end
I could then do the following:
Struct::CoverageClass.any_instance.stubs(:req_subscriber_election_amount).returns(25)
I'm trying to check multiple attributes for nil, I've found this post simplify... but I'm not getting the results I want. I have a user whom I want to update their profile if needed. This user however has all the data I want.
#user.try(:age_id).nil?
#returns false
#user.try(:customer).nil?
#returns false
#user.try(:country).nil?
#returns false
#user.try(:age_id).try(:customer).try(:country).nil?
#returns true
Why is it responding with true here when all the other single instances of tries responds with false?
You are chaining the .try(), which fails after the try(:age_id):
It tries to call age_id on the #user object
if #user.nil? # => returns nil
if #user.age_id != nil # => returns a Fixnum
Then you call the method try(:customer) on a Fixnum which obviously fails # => returns nil
etc.
An example from the IRB console:
1.9.3p448 :049 > nil.try(:nothing).try(:whatever).try(:try_this_also).nil?
=> true
If you want to test that all of these attributes are not nil, use this:
if #user.present?
if #user.age_id.presence && #user.customer.presence && #user.country.presence
# they are all present (!= nil)
else
# there is at least one attribute missing
end
end
In my app a User can create a Business. When they trigger the index action in my BusinessesController I want to check if a Business is related to the current_user.id:
If yes: display the business.
If no: redirect to the new action.
I was trying to use this:
if Business.where(:user_id => current_user.id) == nil
# no business found
end
But it always returns true even when the business doesn't exist...
How can I test if a record exists in my database?
Why your code does not work?
The where method returns an ActiveRecord::Relation object (acts like an array which contains the results of the where), it can be empty but it will never be nil.
Business.where(id: -1)
#=> returns an empty ActiveRecord::Relation ( similar to an array )
Business.where(id: -1).nil? # ( similar to == nil? )
#=> returns false
Business.where(id: -1).empty? # test if the array is empty ( similar to .blank? )
#=> returns true
How to test if at least one record exists?
Option 1: Using .exists?
if Business.exists?(user_id: current_user.id)
# same as Business.where(user_id: current_user.id).exists?
# ...
else
# ...
end
Option 2: Using .present? (or .blank?, the opposite of .present?)
if Business.where(:user_id => current_user.id).present?
# less efficiant than using .exists? (see generated SQL for .exists? vs .present?)
else
# ...
end
Option 3: Variable assignment in the if statement
if business = Business.where(:user_id => current_user.id).first
business.do_some_stuff
else
# do something else
end
This option can be considered a code smell by some linters (Rubocop for example).
Option 3b: Variable assignment
business = Business.where(user_id: current_user.id).first
if business
# ...
else
# ...
end
You can also use .find_by_user_id(current_user.id) instead of .where(...).first
Best option:
If you don't use the Business object(s): Option 1
If you need to use the Business object(s): Option 3
In this case I like to use the exists? method provided by ActiveRecord:
Business.exists? user_id: current_user.id
with 'exists?':
Business.exists? user_id: current_user.id #=> 1 or nil
with 'any?':
Business.where(:user_id => current_user.id).any? #=> true or false
If you use something with .where, be sure to avoid trouble with scopes and better use
.unscoped
Business.unscoped.where(:user_id => current_user.id).any?
ActiveRecord#where will return an ActiveRecord::Relation object (which will never be nil). Try using .empty? on the relation to test if it will return any records.
When you call Business.where(:user_id => current_user.id) you will get an array. This Array may have no objects or one or many objects in it, but it won't be null. Thus the check == nil will never be true.
You can try the following:
if Business.where(:user_id => current_user.id).count == 0
So you check the number of elements in the array and compare them to zero.
or you can try:
if Business.find_by_user_id(current_user.id).nil?
this will return one or nil.
business = Business.where(:user_id => current_user.id).first
if business.nil?
# no business found
else
# business.ceo = "me"
end
I would do it this way if you needed an instance variable of the object to work with:
if #business = Business.where(:user_id => current_user.id).first
#Do stuff
else
#Do stuff
end
Something new to try (:
Assign a variable or return
return unless #business = Business.where(user_id: current_user.id).first
Method would exit at this point if there are no businesses found with current user's ID, or assigns instance variable #business to the first business object.
I am getting this error: undefined method `stringify_keys' for :environ_gross_score:Symbol
when I attempt to create a new rating.
class Rating < ActiveRecord::Base
belongs_to :city
after_save :calculate_rating
def calculate_rating
#env = self.environ
self.city.environ_vote_count += 1
#c = self.city.environ_gross_score
#gross = #c += #env
self.city.update_attributes(:environ_gross_score, #gross )
#hold = self.city.environ_gross_score / self.city.environ_vote_count
self.city.update_attributes(:environ_rating, #hold)
end
end
update_attributes takes a single hash, not 2 parameters. Change the line to:
self.city.update_attributes(:environ_gross_score => #gross)
The error was happening because the method assumed that the first argument passed was a hash, which does (in Rails) respond to stringify_keys.