RSpec Let Analyzer
A Ruby gem that analyzes RSpec test files to identify opportunities for refactoring let and let! declarations to use test-prof's let_it_be helper for improved test performance.
Background
The let_it_be helper from the test-prof gem creates test data once per example group instead of once per example, which can significantly speed up test suites. This tool helps you identify which let declarations are good candidates for conversion to let_it_be.
Installation
Add this line to your application's Gemfile:
gem 'rspec-let-analyzer'And then execute:
bundle installOr install it yourself as:
gem install rspec-let-analyzerUsage
Run the analyzer on your RSpec test suite:
# Analyze specs in current directory
rspec-let-analyzer
# Analyze specs in a different directory
rspec-let-analyzer /path/to/project
rspec-let-analyzer --directory /path/to/projectThis will scan all files matching spec/**/*_spec.rb in the specified directory (or current directory if not specified) and display a table showing:
- Total: Combined score of all metrics (Root + it blocks + Nest columns + Redef + Before Creates)
-
Root: Number of
let/let!declarations at the root level of each describe block - it blocks: Number of test examples in each file
-
Nest 1, Nest 2, etc.: Number of
letdeclarations at different nesting depths -
Redef: Number of
letredefinitions (where a nested context redefines aletfrom a parent scope) -
Bef Cr: Number of
createcalls inbeforeblocks
Command Line Options
# Show top 20 files instead of default 10
rspec-let-analyzer -n 20
# Output in different formats
rspec-let-analyzer --format ascii # Default: ASCII table
rspec-let-analyzer --format json # JSON for further processing
rspec-let-analyzer --format html # HTML report with Bootstrap styling
rspec-let-analyzer --format llm # LLM-friendly JSON for AI-assisted refactoring
# Disable progress indicator
rspec-let-analyzer --no-progress
# Track nesting depth up to 5 levels (default: 3)
rspec-let-analyzer --nesting 5
# Disable nesting tracking entirely
rspec-let-analyzer --no-nesting
# Sort by different fields
rspec-let-analyzer --sort total # Sort by total score (default)
rspec-let-analyzer --sort root # Sort by root lets
rspec-let-analyzer --sort it # Sort by number of it blocks
rspec-let-analyzer --sort redef # Sort by number of redefinitions
rspec-let-analyzer --sort before # Sort by before creates
# Track FactoryBot usage statistics
rspec-let-analyzer --factories
# Analyze a different directory
rspec-let-analyzer --directory /path/to/project
rspec-let-analyzer /path/to/project # Positional argument also works
# Display runtime performance metrics
rspec-let-analyzer --runtime-metrics
# Use a different adapter (default: prism)
rspec-let-analyzer --adapter parser # Requires 'gem install parser'
# Generate reports and save to file
rspec-let-analyzer --format html --output report.html
rspec-let-analyzer --format llm --output refactor-guide.json
# Show help
rspec-let-analyzer --helpLLM-Assisted Refactoring
The --format llm option generates AI-friendly output designed for use with Large Language Models (like Claude, ChatGPT, etc.) to assist with refactoring:
# Generate refactoring guide
rspec-let-analyzer --format llm --output refactor-guide.json -n 10
# Then provide this to your AI assistant with a prompt like:
# "Review the top 5 files in this report and suggest let_it_be refactorings"The LLM format includes:
- Prioritized list of refactoring candidates with scores
- Step-by-step refactoring workflow
- Decision criteria (when to keep/ask/revert changes)
- Guidelines for safe conversions and common fixes
- Context about factory usage patterns
Understanding the Output
High Total score indicates files with the most opportunities for optimization, combining all metrics for a comprehensive view of complexity.
High numbers in the Root column indicate files with many let declarations at the top level. These are prime candidates for let_it_be if:
- The data doesn't change between tests
- The tests don't modify the created objects
High numbers in Redef column indicate files where let declarations are frequently overridden in nested contexts. These may be more complex to refactor since the redefinitions might be intentional for test isolation.
High numbers in Bef Cr column indicate files with many create calls in before blocks, which can be converted to let_it_be or before(:all) blocks.
Factory tracking shows which FactoryBot factories are used most frequently across your test suite, helping identify optimization opportunities.
Example Output
+------------------------------------------------------------+----------+----------+----------+----------+----------+----------+----------+----------+
| File | Total | Root | it blocks| Nest 1 | Nest 2 | Nest 3+| Redef | Bef Cr |
+------------------------------------------------------------+----------+----------+----------+----------+----------+----------+----------+----------+
| spec/models/user_spec.rb | 50 | 12 | 25 | 8 | 3 | 0 | 2 | 0 |
| spec/controllers/posts_controller_spec.rb | 38 | 10 | 18 | 6 | 2 | 1 | 1 | 0 |
+------------------------------------------------------------+----------+----------+----------+----------+----------+----------+----------+----------+
| TOTAL (all 50 files) | 600 | 150 | 300 | | | | 15 | 0 |
+------------------------------------------------------------+----------+----------+----------+----------+----------+----------+----------+----------+
Development
After checking out the repo, run:
bundle installTo run the test suite:
bundle exec rspecTo install this gem onto your local machine:
bundle exec rake installHow It Works
The gem uses Ruby's Prism parser to analyze RSpec files and identify:
-
letandlet!declarations at various nesting levels - Context nesting depth for each declaration
- Cases where
letdeclarations are redefined in nested scopes - FactoryBot usage patterns (when enabled)
This information helps you prioritize which test files to refactor for better performance using let_it_be.
Contributing
Bug reports and pull requests are welcome on GitHub.
License
The gem is available as open source under the terms of the MIT License.