it “increases the user’s reputation” do
lambda { @comment.update_attribute(:helpful, true) }.should
change(@seller.reload,
:reputation_score).by(Event.reputation_change_for(:mark_helpful))
end
And I am getting this error:
Comment comments on posts marking a comment as helpful increases
the user’s reputation
Failure/Error: lambda { @comment.update_attribute(:helpful, true)
}.should change(@seller.reload,
:reputation_score).by(Event.reputation_change_for(:mark_helpful))
reputation_score should have been changed by 3, but was changed
by 0
–
The way the actual code works is, I have a comment observer that does:
def after_update(comment)
Event.create_for_user(comment.user, :mark_helpful)
end
And event.rb does something like:
def create_for_user(user, event_type)
create!(:user => user, :reputation_change =>
Event::SCORES[event_type])
end
The user model has an before_save callback which does:
def sum_points
self.reputation_score = events.sum(:reputation_change)
self.points = events.sum(:points_change)
end
Anyway, so this test fails, and I am not sure why… If I write it in a
slightly less-cool way:
it “increases the user’s reputation” do @seller.reputation_score.should == 0 @comment.update_attribute(:helpful, true) @seller.reload.reputation_score.should ==
Event.reputation_change_for(:mark_helpful)
end
Then it passes… If I throw in a debugger statement in there and
manually
call the code, the reputation_score does indeed increase… So I am
confused why the lambda {}.change thing isn’t working?
On Nov 23, 2011, at 3:33 PM, Patrick J. Collins wrote:
I wrote a test that looked like this:
it “increases the user’s reputation” do
lambda { @comment.update_attribute(:helpful, true) }.should
change(@seller.reload,
:reputation_score).by(Event.reputation_change_for(:mark_helpful))
As you can see, @seller.reload is only evaluated once, and its
reputation score is going to be the same both times. If you want @seller.reload eval’d before and after, then you have to use the block
form:
lambda { @comment.update_attribute(:helpful, true) }.
should change {@seller.reload.reputation_score }.
by(Event.reputation_change_for(:mark_helpful))
Tangent: this is testing two things - @seller.reputation_score and
Event.reputation_change_for(:mark_helpful). If either is failing to work
correctly, this example won’t tell you which. I’d recommend sticking to
literals in expectations:
lambda { @comment.update_attribute(:helpful, true) }.
should change {@seller.reload.reputation_score }.by(3)
As you can see, @seller.reload is only evaluated once, and its reputation
score is going to be the same both times.
Aha… Makes perfect sense. Thanks.
Tangent: this is testing two things - @seller.reputation_score and
Event.reputation_change_for(:mark_helpful). If either is failing to work
correctly, this example won’t tell you which. I’d recommend sticking to
literals in expectations:
lambda { @comment.update_attribute(:helpful, true) }. should change
{@seller.reload.reputation_score }.by(3)
Hmmm… I totally get why you say this, but part of me really hates the
idea of
that 3 someday changing to 5 and then my test breaking, forcing me to
update
the 3 in multiple places.
Would it not be safe to assume that if there’s a test for Event.rb
verifying
the behavior of Event.reputation_change_for, then it’s safe to use that
in a
example? When I originally wrote this, I wanted to stub out the
Event::SCORES
constant and return a hash with just something like:
{ :mark_helpful => { :reputation_change => 123 } }
But I couldn’t figure out how to stub a constant… Of course I could
just
make my test overwrite the mark helpful value of that constant like: