Asserting the last record in table - ruby-on-rails

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

Related

Checking .empty? after destroy not working

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?

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.

failure with creating an instance in specs

I have a model Ticket which has department_id, and Department with
enum name: { dept1: 0, dept2: 1, dept3: 2 }
I have seeded db with these three departments
Department.create(name: :dept1)
Department.create(name: :dept2)
Department.create(name: :dept3)
So I try to write specs on Ticket method
def dept
self.department.name.humanize
end
here is an example
describe '.dept' do
let!(:ticket){ create :ticket, department_id: Department.first.id }
it 'should return right dept' do
expect(ticket.dept).to eq 'Dept1'
end
end
And I recieve an error
ActiveRecord::RecordInvalid:
Validation failed: Department can't be blank
I'm a new guy to rails, so please i9f you don't mind explain me how to write such specs( with seeded db). Any advises would be very useful for me. Thanks!
You'll want to refrain from seeding your database and instead create records that you need for each test.
describe '#dept' do
let(:department) { create :department, title: 'dept1' }
let(:ticket) { build :ticket, department: department }
it 'should return right dept' do
expect(ticket.dept).to eq 'Dept1'
end
end
Notice that I also changed ticket so it's generated by build instead of create. Based on what I see, it doesn't look like you need the overhead of persisting ticket to the database in order to run this particular test.
Also, another small point... But the "convention" (if such a thing exists) is to describe instance methods with hashes in front of them instead of a dot. (Dot denotes a class method.)

Check if attribute contains 2 letters in RSpec

What is the right way to test if the field contains 2 letter string with RSpec ? I am following an old example that I guess worked in rails 2. It creates new Address instance, sets invalid value on it, and then trigger valid? on that instance and finally checks if the errors report something wrong.
it 'requires state to be of length 2' do
subject = Address.new
subject.state = 'Cal'
should_not be_valid
subject.errors.on(:state).should_not be_nil
end
Now, Rails 3 doesn't have errors.on, so I tried with
subject.errors[:state].should_not be_nil
But the problem here is that errors[:attribute] is empty Array instead of nil.
You can still say
subject.errors[:state].should_not be_empty
Validation errors are now in errors.messages
errors.messages.should be_present

Moching rails association methods

Here is my helper method which I want to test.
def posts_correlation(name)
if name.present?
author = User.find_by_name(name)
author.posts.count * 100 / Post.count if author
end
end
A factory for user.
factory :user do
email 'user#example.com'
password 'secret'
password_confirmation { password }
name 'Brian'
end
And finally a test which permanently fails.
test "should calculate posts count correlation" do
#author = FactoryGirl.create(:user, name: 'Jason')
#author.posts.expects(:count).returns(40)
Post.expects(:count).returns(100)
assert_equal 40, posts_correlation('Jason')
end
Like this.
UsersHelperTest:
FAIL should calculate posts count correlation (0.42s)
<40> expected but was <0>.
test/unit/helpers/users_helper_test.rb:11:in `block in <class:UsersHelperTest>'
And the whole problem is that mocha doesn't really mock the count value of author's posts, and it returns 0 instead of 40.
Are there any better ways of doing this: #author.posts.expects(:count).returns(40) ?
When your helper method runs, it's retrieving its own object reference to your author, not the #author defined in the test. If you were to puts #author.object_id and puts author.object_id in the helper method, you would see this problem.
A better way is to pass the setup data for the author in to your mocked record as opposed to setting up expectations on the test object.
It's been a while since I used FactoryGirl, but I think something like this should work:
#author = FactoryGirl.create(:user, name: 'Jason')
(1..40).each { |i| FactoryGirl.create(:post, user_id: #author.id ) }
Not terribly efficient, but should at least get the desired result in that the data will actually be attached to the record.

Resources