Using Let and Context to Modularize RSpec Tests
When test suites contain a lot of duplication, coupling occurs at an individual spec basis. By utilizing core RSpec functionality, developers can clean up specs in order to reduce duplication and add clarity to the focal point of the spec.
Ally Piechowski · · 2 min read
When test suites contain a lot of duplication, coupling occurs at an individual spec basis. By utilizing core RSpec functionality, developers can clean up specs in order to reduce duplication and add clarity to the focal point of the spec.
If I want to test an invalid and valid email, it’s pretty easy to test with a basic RSpec test:
RSpec.describe User do
it '[email protected] is a valid email' do
expect(
User.new(
name: 'John Doe',
email: '[email protected]',
phone: '555-555-5555'
)
).to be_valid
end
it 'johnatexampledotcom is not a valid email' do
expect(
User.new(
name: 'John Doe',
email: 'johnatexampledotcom',
phone: '555-555-5555'
)
).to_not be_valid
end
end
While this may work for the first few specs, this starts to get a bit tedious and duplicative.
This is not optimal because our setup code has a lot of duplication and this duplicated code.
The RSpec Let + Context Pattern
By using core Rspec functionality, we can spruce up these 2 tests like so:
RSpec.describe User do
subject do
User.new(
name: 'John Doe',
email: email,
phone: '555-555-5555'
)
end
let(:email) { '[email protected]' }
context 'when email is [email protected]' do
let(:email) { '[email protected]' }
it do
expect(subject).to be_valid
end
end
context 'when email is testfakeemail (does not contain @)' do
let(:email) { 'testfakeemail' }
it do
expect(subject).to_not be_valid
end
end
end
This provides us with:
- DRY specs
- Re-usable setup code
- Additional specs within each context
- New specs, such as “phone number validations”
- Easily interpretable differences between specs
The context / let pattern brings the most clarity into the differences between the 2 tests. This pattern can be used with in combination with Factories in order to make my tests extra clear and extra dry. Additionally, this pattern typically results in a more thoughtful test output.
Related Articles
- How I Audit a Legacy Rails Codebase in the First Week
- Rails default_scope: Why You Should Never Use It
- Solved: Warning: Using the last argument as keyword parameters is deprecated
- Rails: How to Use Greater Than/Less Than in Active Record where Statements
- Migrating from Sprockets to Propshaft: Is It Worth It?