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).