This topic might seem straightforward at first glance—it just makes sense intuitively. But is there an actual difference? Let’s delve into the details and examine what is really happening behind the scenes.
require "rails_helper"
RSpec.describe User do
describe "associations" do
subject(:user) { create(:user) }
it do
expect(user).to have_many(:carts)
end
end
end
RubyRSpec.describe User do
describe "associations" do
let(:user) { create(:user) }
it do
expect(user).to have_many(:carts)
end
end
end
RubyThese two approaches are essentially the same. On the backend, we create the user for the test, establish its associations, run the test, and then roll back the database.
There’s an obvious advantage when it comes to subjects: we get the Shoulda matchers, which come with their own pros and cons. Understanding when to use a subject becomes more crucial due to the ambiguity surrounding it, especially in terms of where it’s built and the contexts in which you’re using it. RuboCop enforces naming your subjects, which is something I’m uncertain about. Depending on how your test is set up, the subject should be evident.
One notable difference when discussing subjects and lets in the context of associations and Shoulda matchers is that defining the subject might actually create the object rather than just building it internally. Let’s compare the two examples before and after to illustrate this.
RSpec.describe User do
subject(:user) { create(:user) }
it { is_expected.to have_many(:carts) }
end
RubyRSpec.describe User do
let(:user) { create(:user) }
it { is_expected.to have_many(:carts) }
end
RubyAt first glance, these two approaches should essentially achieve the same result. Both pass their tests without any issues. However, the key difference is that the subject
will create the user for the test, whereas the let
variable, if never called, means RSpec will use the described class, User
, without formally creating it.
This seemingly small difference can significantly impact the speed of tests when examined more closely. Considering how extensive a user spec file can be, with all its associations and tests, using subject
might actually cause a major slowdown.
# subject definition
Finished in 0.23173 seconds (files took 9.77 seconds to load)
1 example, 0 failures
#vs the let definiton
Finished in 0.15198 seconds (files took 10.29 seconds to load)
1 example, 0 failures
RubySo even though there’s no technical difference in calling a subject
vs. a let
variable, it does make a slight difference when we use Shoulda matchers and when we decide to create objects. Even for that one test, we should expect a significant improvement in test performance if we stop using subject
for tests that don’t actually require objects to be built.
Leave a Reply