Testing speedbump: RSpec and ActiveRecord reload
As part of a pair project with Philly’s newest podcast host, Steven Rayesky, I helped to create a Rails application called Three Good Things. We only had a few days to propose an idea and build a prototype, but that made for a fun (and exhausting) few days of hacking.
Three Good Things
Now that the smoke has cleared, I’ve been looking for ways to improve the codebase every day. One weakness in our original protoype was the lack of tests (!), so I’ve been using this as an opportunity to learn and apply some solid testing principles.
Skipping tests originally seemed reasonable idea—we were under a tight deadline and were struggling with enough already—but that decision has haunted me since. Several times, I’ve refactored some code or fixed a bug only to break something else without realizing it. Lesson learned — I submit to the ways of testing.
My first testing speedbump
Following along with Everyday Rails Testing with RSpec, I started by writing tests for our models, their instance, and their class methods. Everything was going smoothly until I came to test a simple class method for the User model,
This method iterates through every
user and calls
#reset_streak on each one. The
#reset_streak instance method checks to see whether a user has written at least three good things on the current day, and if not, it resets their
current_streak attribute to 0.
In one of my tests for the class method
reset_all_streaks, I wanted to confirm that if a user had a
current_streak of 3, but then failed to write at least three things (here, they only write two things), then their current streak will be set to 0 after calling
It failed! I was expecting the user’s streak to be reset to 0, but it was still stuck at 3 after calling
reset_all_streaks. More confusing, the tests for the instance method
#reset_streak were working as expected, and
reset_all_streaks seemed to be working if I littered it with
puts statements. Somehow, the streak was getting reset as expected when
reset_all_streaks was called during the test, but that change was getting reverted before I checked it with the Rspec matcher.
Reload to the rescue
After a lot of head-scratching and many, many
puts later, the best thing happened: I found a StackOverflow post with a similar issue.
The problem: while the class method does update the record in the database table (explaining why
puts suggested it was working), the instance variable
user within the RSpec block doesn’t get updated. It needs to be reloaded after the class method does its thing for the changes to be reflected in the
The solution was to call ActiveRecord’s reload on
user before (or within) the
expect, like so:
Now it works as expected, and I can carry on with testing.