Reputation: 9691
RSpec newbie here.
I'm trying to test my models, which have methods that uses update_attributes
to update values of other models.
I'm sure that the values are persisted in the database, but they are not passing the specs.
However, when I include something like @user.reload
it works.
I'm wondering if I'm doing things wrongly. Specifically, how should one test models which change attributes of other models?
UPDATED with code:
describe Practice do
before(:each) do
@user = build(:user)
@user.stub!(:after_create)
@user.save!
company = create(:acme)
course = build(:acme_company, :company => company)
course.save!
@practice = create(:practice, :user_id => @user.id, :topic => "Factors and Multiples" )
end
describe "#marking_completed" do
it "should calculate the points once the hard practice is done" do
@practice.question_id = Question.find(:first, conditions: { diff: "2" }).id.to_s
@practice.responses << Response.create!(:question_id => @practice.question_id, :marked_results => [true,true,true,true])
@practice.save!
lambda {
@practice.marking_completed
@practice.reload # <-- Must add this, otherwise the code doesn't work
}.should change { @practice.user.points }.by(20)
end
end
end
def marking_completed
# Update user points
user.add_points(100)
self.completed = true
self.save!
end
def add_points(points)
self.points += points
update_attributes(:points => self.points)
end
Upvotes: 4
Views: 4764
Reputation: 51697
What's going on is that the user object is cached on the @practice variable, thus requiring a reload when user has been updated. There isn't much you can do about this with the current spec, but you might want to think about what you're actually testing. It seems weird to me that your spec is for the Practice model, yet the assertion should change { @practice.user.points }.by(20)
is really describing the User behavior.
I would personally decouple the the Practice and User models a little more in your spec, and test their behavior independently.
describe "#marking_completed" do
before do
@practice.question_id = Question.find(:first, conditions: { diff: "2" }).id.to_s
@practice.responses.create!(:question_id => @practice.question_id, :marked_results => [true,true,true,true])
@practice.save!
end
it "should calculate the points once the hard practice is done" do
@practice.user.should_receive(:add_points).with(20).once
@practice.marking_completed
end
end
Then I would add a separate test for the User model:
describe User do
it 'should add specified points to a user' do
lambda { subject.add_points(100) }.should change { subject.points }.by(100)
end
end
One other sort of side note is that it's not clear what Question.find
is returning, or why the user's points changes by 20 in your test.
Upvotes: 5