be-let-it-be
A command-line tool that automatically converts RSpec's let and let! declarations to let_it_be where it's safe to do so. The tool runs your tests after each conversion to ensure they still pass, making the optimization process safe and reliable.
Motivation
One of the main motivations is to improve test speed.
What is let_it_be?
let_it_be is a helper provided by the test-prof gem that caches test data across examples instead of recreating it for each test. This can significantly improve test performance, especially for expensive object creation like database records.
Performance Benefits
Using let_it_be can significantly improve test performance by:
- Reducing database queries
- Minimizing object instantiation overhead
- Sharing immutable test data across examples
Performance gains are especially noticeable with:
- Factory-created database records
- Complex object initialization
- Large test suites
How It Works
- Parse: Analyzes your RSpec file using Ruby's Abstract Syntax Tree
- Identify: Finds all
letandlet!declarations - Convert & Test: For each declaration:
- Converts it to
let_it_be - Runs your tests
- Keeps the change if tests pass, reverts if they fail
- Save: Writes the successfully converted file, or it outputs the result if it's in dryrun mode
Example Conversion
Before:
RSpec.describe User do
let!(:admin) { create(:user, admin: true) }
let(:user) { create(:user) }
let(:posts) { user.posts }
let(:mutable_array) { [] }
it "modifies the array" do
mutable_array << 1
expect(mutable_array).to eq([1])
end
endAfter:
RSpec.describe User do
let_it_be(:admin) { create(:user, admin: true) }
let_it_be(:user) { create(:user) }
let(:posts) { user.posts } # Kept as 'let' due to dependency
let(:mutable_array) { [] } # Kept as 'let' because tests modify it
it "modifies the array" do
mutable_array << 1
expect(mutable_array).to eq([1])
end
endInstallation
Add this line to your application's Gemfile:
group :development, :test do
gem 'be-let-it-be'
endAnd then execute:
$ bundle installOr install it yourself as:
$ gem install be-let-it-bePrerequisites
- Ruby 3.3.0 or higher
- Your project must have test-prof installed, as
let_it_beis a feature provided by that gem
Usage
Basic Usage
Convert a single spec file:
be-let-it-be convert spec/models/user_spec.rbOptions
-
--dryrun- Show what would be converted without making actual changes -
--dryrun-exit-code- Exit code to use in dryrun mode when any convertible declarations are present (default: 1) -
--verbose- Display detailed processing information -
--rspec_cmd- Customize the RSpec command used for verification (default: "rspec")
Examples
# Dry-run to preview changes
be-let-it-be convert spec/models/user_spec.rb --dryrun
# Verbose output for debugging
be-let-it-be convert spec/models/user_spec.rb --verbose
# Use custom RSpec command
be-let-it-be convert spec/models/user_spec.rb --rspec_cmd="rspec --format progress"When NOT to Use let_it_be
The tool automatically detects when conversions would break tests, but it's good to understand when let_it_be isn't appropriate:
- Mutable objects: When tests modify the object state
- Test-specific state: When the value depends on test-specific setup
- Fresh instances: When each test requires a completely new instance
Current Limitations
- Originally, the replacement of
letandlet!should adopt the combination that provides the most optimized execution time. However, attempting to do so would likely cause combinatorial explosion, making it non-trivial to implement. Therefore, we currently use a simple approach of replacing them in order of appearance. We plan to address this optimization in the future.
Development
After checking out the repo, run bin/setup to install dependencies. Then, run rake test to run the tests.
To install this gem onto your local machine, run bundle exec rake install.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/moznion/be-let-it-be. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.
License
The gem is available as open source under the terms of the MIT License.
Code of Conduct
Everyone interacting in the be-let-it-be project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.