I'm working on the exercises for chapter 6 of Hartl's Rails 4 Tutorial. The first exercise tests to make sure that user email addresses are down-cased correctly:
require 'spec_helper'
describe User do
.
.
.
describe "email address with mixed case" do
let(:mixed_case_email) { "Foo@ExAMPle.CoM" }
it "should be saved as all lower-case" do
@user.email = mixed_case_email
@user.save
expect(@user.reload.email).to eq mixed_case_email.downcase
end
end
.
.
.
end
What I don't understand is why the 'reload' method is necessary here. Once @user.email
is set to the contents of mixed_case_email
and saved, aren't @user.reload.email
and @user.email
the same thing? I took the reload method out just to try it and it didn't seem to change anything with the test.
What am I missing here?
Yes in this case @user.reload.email
and @user.email
is the same thing. But it's good practice to use @user.reload.email
instead of @user.email
to check what is exactly saved in the database i mean you don't know if you or someone add some code in after_save which changes it's value then it will not have effect on your tests.
EDIT:
And also what you are checking is what's saved in the database so @user.reload.email
exactly reflects what's saved in database then @user.email
In-Memory vs. Database
It's important to understand the difference from in-memory and the database. Any ruby code you write is in-memory. For instance, whenever a query is executed, it creates a new in-memory object with the corresponding data from the database.
# @student is a in-memory object representing the first row in the Students table.
@student = Student.first
Your example
Here is your example with comments for explanation
it "should be saved as all lower-case" do
# @user is an in-memory ruby object. You set it's email to "Foo@ExAMPle.CoM"
@user.email = mixed_case_email
# You persist that objects attributes to the database.
# The database stores the email as downcase probably due to a database constraint or active record callback.
@user.save
# While the database has the downcased email, your in-memory object has not been refreshed with the corresponding values in the database.
# In other words, the in-memory object @user still has the email "Foo@ExAMPle.CoM".
# use reload to refresh @user with the values from the database.
expect(@user.reload.email).to eq mixed_case_email.downcase
end
To see a more thorough explanation see this post.
reload
Reloads the attributes of object(here @user) from the database. It always ensures object has latest data that is currently stored in database.
With this we can also avoid
ActiveRecord::StaleObjectError
This normally comes when we try to change old version of the object.
It should be the same thing. The whole point is that the reload method reloads the object from the database. Now you can check if your newly created test object is actually saved with the correct/expected attributes.
What the example wants to check is whether the before_save
callback in app/models/user.rb
does its job. The before_save
callback should set every user's email to downcase before it is saved in the database, thus chapter 6 exercise 1 wants to test if its value in the database, that can be retrieved by using method reload
, is effectively saved as downcase.