Rails Testing: Reload the Model
Photo of my stapler
Throughout my time using Rails, I’ve had the pleasure of working with people who are new to Ruby on Rails. This post is a quick one that outlines a testing issue that comes up every now and then.
To keep things short, let’s jump right into the code!
class Task < ApplicationRecord;
# Has a bolean database column`archive`
end
class ArchiverService
def self.call
Task.update_all(archive: true)
end
end
We have an ActiveRecord model and a service class that updates a value on the model. This is very much a contrived example to illustrate the problem.
require "test_helper"
class ArchiverServiceTest < ActiveSupport::TestCase
test "archives task" do
task = Task.create(archive: false)
ArchiverService.call
assert(task.archive) # fails!
end
end
This test fails.
The problem here is that the task
object in the test isn’t the same as the one being updated in the ArchiverService
. When a model is instantiated, its attributes are cached. If a value is updated and persisted in the database, the changes aren’t propagated to other instances of that model.
The solution here is to add in a task.reload
after the ArchiverService.call
, as we want the task’s values to be refreshed with what the database has at that point. If you want to read more into the #reload
method, you can look at its docs and source. It’s worth noting that the documentation states the following:
Reloads the record from the database.
…
Reloading is commonly used in test suites to test something is actually written to the database, or when some action modifies the corresponding row in the database but not the object in memory
So be aware of this situation and recall that you do have access to #reload
if you need to freshen up your model’s attributes from the database. Rails even provide the ability to #reload_association
if your model has them (see docs here).