Checking .empty? after destroy not working - ruby-on-rails

I wrote a test that checks for referral's from user's within the same company. In the assertions I am checking that the referrals are not empty, then running the destroy_referrals method before finally checking that the referrals are empty. assert referrals.empty? is returning a failure,
1) Failure:
CompanyTest#test_destroy_referrals_with_referrals [test/models/company_test.rb:634]:
Expected false to be truthy.
company_test.rb
def test_destroy_referrals_with_referrals
company = companies(:default)
referrals = company.users.map {|u| u.referrals unless u.referrals.empty?}.uniq.compact
assert !referrals.empty?
company.destroy_referrals
assert referrals.empty?
end
I was expecting that the last assertion would confirm that the referrals have been deleted. Any ideas why this throws a failure?

The problem is that you have prepared the referrals in advance and never changed it. To make the test to pass, you should reload them:
def test_destroy_referrals_with_referrals
company = companies(:default)
referrals = -> { company.users.map(&:referrals).reject(&:empty?) }
assert !referrals.().empty?
company.destroy_referrals
assert referrals.().empty?
end

Referrals is an array object. You loaded it once, it stays in memory. Why would you expect it to change?
Re-read it.
referrals = company.users.map {|u| u.referrals unless u.referrals.empty?}.uniq.compact
assert !referrals.empty?
company.destroy_referrals
company.reload # just for good measure
referrals = company.users.map {|u| u.referrals unless u.referrals.empty?}.uniq.compact
assert referrals.empty?

Related

Increment field within validator

I have a custom validator that checks if the user has entered the correct SMS code. When the user enters the wrong code I need to log the failed attempt and limit their retries to 3 per code.
I have created the following validator that works however the field is not being incremented.
def token_match
if token != User.find(user_id).verification_token
User.find(user_id).increment!(:verification_fails)
errors.add(:sms_code, "does not match")
end
end
The problem is as soon as I add the error the previous statement is rolled back. If I comment out the errors.add line then the increment works however there is no higher level validation performed.
Change your custom validator to be:
def token_match
if token != User.find(user_id).verification_token
errors.add(:sms_code, "does not match")
end
end
and add in your model after_validation callback to be like this:
after_validation: increase_fails_count
def increase_fails_count
unless self.errors[:sms_code].empty?
user = User.find_by(:id => user_id)
user.increment!(:verification_fails)
user.save
end
end
You can use #update_columns in your validator. It writes directly to db.
u = User.find(user_id)
u.update_columns(verification_fails: u.verification_fails + 1)
This worked for me. But if for some reason it doesn't work for you, maybe you can try running it in a new thread,which creates a new db connection:
Thread.new do
num = User.find(user_id).verification_fails
ActiveRecord::Base.connection_pool.with_connection { |con| con.exec_query("UPDATE users SET verification_fails = #{num} WHERE id = #{user_id}") }
end.join

the right way to change the associated object in rspec

I recently started to test with rspec, so I can strongly be mistaken, correct me if there is a better way
I create two related models
let(:user) {FactoryGirl.create :user}
let!(:participation) {FactoryGirl.create :participation, user: user}
and before one of the tests change one of the related objects
context "when" do
before {participation.prize = 100}
it "" do
binding.pry
end
end
But inside it
participation.prize => 100
user.participatons.select(:prize) => nil
what am I doing wrong ? and how to fix it?
When you say user.participations.select(:prize), you're making a query to the db to get values in the user's participations' prize columns. But when you say before {participation.prize = 100} you're only setting the prize attribute on the participation object. Try saving the participation before the select line:
participation.prize # => 100
participation.save
user.participatons.select(:prize) # => nil
Another possible issue is that user.participations has been memoized by a previous call. Ensure that user.participations.first == participation. If it doesn't, check
1) puts participation.user_id and
2) puts user.participations, user.reload.participations
Lastly, a better way of setting up the test so that you run into this issue less often is something along the lines of:
# let(:price) { 0 } # default price. Optional so that tests won't throw errors if you forget to set it in a context/describe block.
let(:user) {FactoryGirl.create :user}
let!(:participation) {FactoryGirl.create :participation, user: user, price: price}
# ...
context "when ..." do
let(:price) { 100 }
it "" do
binding.pry
end
end
This way, the price is set when you create the model. Following this pattern generally means running into this problem less.

RSpec 3 - raising an error on purpose to test it is rescued

Consider a simple method -
def my_method(users)
eligible_users = []
users.each do |u|
# Go to the next user unless they are eligible
next unless is_eligible?(u)
begin
update_user(u)
eligible_users << u
rescue
puts "Error occured"
# Prints some other stuff about error
next
end
end
end
A key feature of this method is that it loops through users but continues to the next user even if a given user throws an error.
If I were writing spec tests for this, I'd love to pass an array of 3 users and purposely have it error out on the first user. I can then check that the 2nd and 3rd were still correctly processed.
How would I go about raising an error on purpose for only one of the result sets?
I was thinking I could stub is_eligible? and return an error for one of the result and true for the remainder -
allow_any_instance_of(MyClass).to receive(:is_eligible?).and_return(
raise StandardError.new,
true,
true
)
As expected, that doesn't work. Any other approaches?
Thanks!
I can't exactly answer the question, but rather than doing a begin resuce thing,you can follow this approach,
Make the update_user return true or false.
Keep an array of users that falied to update.
return an object like
response: { status: "failure", message: "falied to update #{pluralize(failed_users_array.count, 'user')}", failures: failed_users_array.join(", ") } for failures and
response: { status: "success", message: "#{pluralize(users.count, 'user')} updated successfully" } for all success.
now you can easily test,
have two cases, one where you can test failures and when you can test all success.
Just stub the response object.
For raising errors, you have to do .and_raise("some tezt or StandardError.new") , thats in the docs.

Will page.has_content? wait?

I'm just wondering if I'm doing
click_on('Open')
page.has_content?("foo")
click_on("Done")
Will Capybara know to wait until the page has content 'foo' or it's just going to continue on to click "Done" regardless of the return value for page.has_content?('foo')???
Based on the code, I believe that has_content? should wait. If I recall correctly, that is what the synchronize part is for.
def has_text?(*args)
query = Capybara::Query.new(*args)
synchronize(query.wait) do
raise ExpectationNotMet unless text_found?(*args)
end
return true
rescue Capybara::ExpectationNotMet
return false
end
alias_method :has_content?, :has_text?

Asserting the last record in table

I'm trying to assert that the last record did not get deleted in rails model unit test. I raise an exception if the record.count.one? is true. Initially there are two records.
Edited:
There is a user story that says you can delete the users.
You cannot delete the user that you are logged in with. (functional test)
You cannot delete the last user. (unit test)
here it is:
test "verify cannot destroy last user" do
assert_raise(RuntimeError) {
User.find(:all).select {|u| u.destroy} }
assert_equal 1, User.count
end
Here's my literal translation of what you are asking (I think):
last_user = User.last
...
assert_equal last_user, User.last
Here's more traditional test code that is a bit less fragile:
assert_difference('User.count',-1) do
...
end
(But Gutzofter may actually be onto what you're looking for.)

Resources