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
In Practice.rb
def marking_completed
# Update user points
user.add_points(100)
self.completed = true
self.save!
end
In User.rb
def add_points(points)
self.points += points
update_attributes(:points => self.points)
end
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.
Then I would add a separate test for the User model:
One other sort of side note is that it’s not clear what
Question.findis returning, or why the user’s points changes by 20 in your test.