Initial commit
This commit is contained in:
192
skills/ruby-test-analyzer/SKILL.md
Normal file
192
skills/ruby-test-analyzer/SKILL.md
Normal file
@@ -0,0 +1,192 @@
|
||||
# Ruby Test Analyzer Skill
|
||||
|
||||
Intelligently analyze RSpec test failures and provide actionable debugging guidance.
|
||||
|
||||
## When to Activate
|
||||
|
||||
This skill activates when:
|
||||
- RSpec tests fail
|
||||
- User runs test commands
|
||||
- User asks about test failures or debugging
|
||||
- Working with test files
|
||||
|
||||
## Core Capabilities
|
||||
|
||||
### 1. Failure Pattern Recognition
|
||||
|
||||
Automatically identify common failure patterns:
|
||||
|
||||
**NoMethodError Patterns:**
|
||||
- Nil object errors → Check object initialization
|
||||
- Missing method → Check for typos or missing implementation
|
||||
- Wrong receiver → Check factory setup or test doubles
|
||||
|
||||
**Expectation Failures:**
|
||||
- Off-by-one errors → Check loop boundaries
|
||||
- Wrong values → Check business logic implementation
|
||||
- Type mismatches → Check data transformation
|
||||
|
||||
**Database/State Issues:**
|
||||
- Transactional rollback problems
|
||||
- Factory/fixture conflicts
|
||||
- Test interdependencies
|
||||
|
||||
### 2. Provide Debugging Steps
|
||||
|
||||
For each failure, suggest:
|
||||
|
||||
1. **Immediate Checks:**
|
||||
- Verify object is not nil
|
||||
- Check method spelling
|
||||
- Confirm proper setup in before blocks
|
||||
|
||||
2. **Investigation Steps:**
|
||||
- Add `puts` or `binding.pry` at strategic points
|
||||
- Check factory definitions
|
||||
- Verify database state
|
||||
|
||||
3. **Likely Fixes:**
|
||||
- Code examples showing the fix
|
||||
- References to related files
|
||||
- Design improvements to prevent recurrence
|
||||
|
||||
### 3. Test Quality Analysis
|
||||
|
||||
Evaluate test quality:
|
||||
|
||||
**Good Signs:**
|
||||
- Clear describe/context structure
|
||||
- Single responsibility per test
|
||||
- Proper use of let/let!/before
|
||||
- Descriptive test names
|
||||
- Good isolation
|
||||
|
||||
**Warning Signs:**
|
||||
- Tests with multiple expectations
|
||||
- Tests depending on execution order
|
||||
- Overuse of let! (eager evaluation)
|
||||
- Brittle tests (too coupled to implementation)
|
||||
- Missing edge cases
|
||||
|
||||
### 4. Suggest Missing Tests
|
||||
|
||||
Identify untested scenarios:
|
||||
- Edge cases
|
||||
- Error conditions
|
||||
- Boundary values
|
||||
- Integration points
|
||||
- State transitions
|
||||
|
||||
## Example Analysis
|
||||
|
||||
**Failure Detected:**
|
||||
```
|
||||
Failure/Error: expect(user.full_name).to eq("John Doe")
|
||||
expected: "John Doe"
|
||||
got: "John"
|
||||
```
|
||||
|
||||
**Analysis Response:**
|
||||
|
||||
**Failure: User#full_name incomplete**
|
||||
- **Location:** spec/user_spec.rb:23
|
||||
- **Error Type:** Expectation mismatch
|
||||
|
||||
**Immediate Cause:**
|
||||
The `full_name` method is only returning the first name.
|
||||
|
||||
**Investigation Steps:**
|
||||
1. Check the implementation at lib/user.rb:15
|
||||
```ruby
|
||||
# Current implementation (likely):
|
||||
def full_name
|
||||
first_name
|
||||
end
|
||||
```
|
||||
|
||||
2. Verify test data:
|
||||
```ruby
|
||||
# Check that your factory/test setup has both names
|
||||
user = User.new(first_name: "John", last_name: "Doe")
|
||||
```
|
||||
|
||||
**Suggested Fix:**
|
||||
```ruby
|
||||
# lib/user.rb:15
|
||||
def full_name
|
||||
[first_name, last_name].compact.join(' ')
|
||||
end
|
||||
```
|
||||
|
||||
**Additional Considerations:**
|
||||
- Handle nil values gracefully (using `compact`)
|
||||
- Consider edge cases: missing last name, middle names
|
||||
- Add test for user with only first name
|
||||
|
||||
**Suggested Additional Tests:**
|
||||
```ruby
|
||||
context 'when last_name is missing' do
|
||||
it 'returns only first_name' do
|
||||
user = User.new(first_name: "John", last_name: nil)
|
||||
expect(user.full_name).to eq("John")
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## Performance Analysis
|
||||
|
||||
When tests are slow:
|
||||
|
||||
**Identify Bottlenecks:**
|
||||
1. Run with profiling: `bundle exec rspec --profile`
|
||||
2. Find slowest examples
|
||||
3. Categorize delays:
|
||||
- Database operations
|
||||
- External service calls
|
||||
- Complex computations
|
||||
- Factory creation
|
||||
|
||||
**Optimization Suggestions:**
|
||||
|
||||
**Database-Heavy Tests:**
|
||||
- Use `build_stubbed` instead of `create`
|
||||
- Use `let` instead of `let!` where possible
|
||||
- Consider using transactions or database_cleaner strategies
|
||||
|
||||
**External Services:**
|
||||
- Stub external API calls
|
||||
- Use VCR for HTTP interactions
|
||||
- Mock time-consuming operations
|
||||
|
||||
**Factory Optimization:**
|
||||
- Minimize associated records
|
||||
- Use traits for specific scenarios
|
||||
- Consider using `build` instead of `create`
|
||||
|
||||
## Interactive Debugging
|
||||
|
||||
Offer to:
|
||||
1. Add debugging output to specific lines
|
||||
2. Insert `binding.pry` at failure point
|
||||
3. Show related code context
|
||||
4. Run specific tests in isolation
|
||||
5. Check factory definitions
|
||||
6. Verify test data setup
|
||||
|
||||
## Test Coverage Suggestions
|
||||
|
||||
Suggest tests for:
|
||||
- **Happy path**: Normal expected usage
|
||||
- **Edge cases**: Boundary conditions
|
||||
- **Error cases**: Invalid inputs
|
||||
- **Null cases**: Nil/empty values
|
||||
- **Integration**: Cross-object interactions
|
||||
|
||||
## Guidelines
|
||||
|
||||
- Focus on actionable next steps
|
||||
- Provide specific line numbers and file paths
|
||||
- Show concrete code examples
|
||||
- Explain the "why" behind failures
|
||||
- Suggest preventive measures
|
||||
- Balance speed of fix with quality of solution
|
||||
131
skills/ruby-test-analyzer/scripts/README.md
Normal file
131
skills/ruby-test-analyzer/scripts/README.md
Normal file
@@ -0,0 +1,131 @@
|
||||
# Test Analyzer Scripts
|
||||
|
||||
Executable shell scripts for running and analyzing RSpec tests.
|
||||
|
||||
## Scripts
|
||||
|
||||
### run_tests.sh
|
||||
Run RSpec tests with intelligent filtering.
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
./run_tests.sh all
|
||||
|
||||
# Run only fast tests (exclude slow)
|
||||
./run_tests.sh fast
|
||||
|
||||
# Run only previously failed tests
|
||||
./run_tests.sh failures
|
||||
|
||||
# Run tests for changed files
|
||||
./run_tests.sh changed
|
||||
|
||||
# Profile slowest tests
|
||||
./run_tests.sh profile
|
||||
|
||||
# Run specific file
|
||||
./run_tests.sh spec/models/user_spec.rb
|
||||
```
|
||||
|
||||
**Modes:**
|
||||
- `all` - All tests with documentation format
|
||||
- `fast` - Exclude tests tagged with :slow
|
||||
- `failures` - Only re-run failed tests (uses .rspec_status)
|
||||
- `changed` - Auto-detect changed files via git diff
|
||||
- `profile` - Show 10 slowest examples
|
||||
- `<file_path>` - Run specific test file
|
||||
|
||||
**Features:**
|
||||
- Auto-detects RSpec availability
|
||||
- Helpful error messages
|
||||
- Smart test file detection from lib/ files
|
||||
- Documentation format for readability
|
||||
|
||||
### analyze_failures.sh
|
||||
Analyze test failures and provide debugging hints.
|
||||
|
||||
```bash
|
||||
./analyze_failures.sh
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```
|
||||
Results: 3 failures out of 47 tests
|
||||
|
||||
Failed examples:
|
||||
|
||||
User#full_name returns first and last name
|
||||
File: spec/user_spec.rb:23
|
||||
Error: RSpec::Expectations::ExpectationNotMetError
|
||||
Message: Expected "John Doe" but got "John"
|
||||
|
||||
...
|
||||
|
||||
Common fixes:
|
||||
- Check for typos in method names
|
||||
- Verify test data setup (factories, fixtures)
|
||||
- Check for nil objects
|
||||
- Review recent code changes
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- Parses JSON test results
|
||||
- Groups failures by type
|
||||
- Provides file:line references
|
||||
- Suggests common fixes
|
||||
- Shows debugging commands
|
||||
|
||||
**Requires:**
|
||||
- jq (JSON processor)
|
||||
- RSpec with JSON formatter
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Quick Test Run
|
||||
```bash
|
||||
# Run changed tests
|
||||
./run_tests.sh changed
|
||||
|
||||
# If failures, analyze
|
||||
./analyze_failures.sh
|
||||
```
|
||||
|
||||
### TDD Workflow
|
||||
```bash
|
||||
# Run all tests
|
||||
./run_tests.sh all
|
||||
|
||||
# Profile to find slow tests
|
||||
./run_tests.sh profile
|
||||
|
||||
# Tag slow tests, then run fast
|
||||
./run_tests.sh fast
|
||||
```
|
||||
|
||||
### CI/CD
|
||||
```bash
|
||||
# In CI pipeline
|
||||
./run_tests.sh all || ./analyze_failures.sh
|
||||
```
|
||||
|
||||
## RSpec Configuration
|
||||
|
||||
For best results, add to `.rspec`:
|
||||
```
|
||||
--format progress
|
||||
--require spec_helper
|
||||
--example-status-persistence-file-path .rspec_status
|
||||
```
|
||||
|
||||
And in `spec/spec_helper.rb`:
|
||||
```ruby
|
||||
RSpec.configure do |config|
|
||||
config.example_status_persistence_file_path = ".rspec_status"
|
||||
end
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
- RSpec gem
|
||||
- jq (for analyze_failures.sh)
|
||||
- git (for changed mode)
|
||||
42
skills/ruby-test-analyzer/scripts/analyze_failures.sh
Executable file
42
skills/ruby-test-analyzer/scripts/analyze_failures.sh
Executable file
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env bash
|
||||
# Analyze RSpec failures and provide debugging hints
|
||||
|
||||
set -e
|
||||
|
||||
echo "🔍 Analyzing test failures..."
|
||||
|
||||
# Run tests and capture output
|
||||
OUTPUT=$(bundle exec rspec --format json --out tmp/rspec_results.json --format progress 2>&1) || true
|
||||
|
||||
if [ ! -f "tmp/rspec_results.json" ]; then
|
||||
echo "No test results found. Run tests first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Parse JSON results
|
||||
FAILURES=$(jq '.summary.failure_count' tmp/rspec_results.json)
|
||||
TOTAL=$(jq '.summary.example_count' tmp/rspec_results.json)
|
||||
|
||||
echo ""
|
||||
echo "Results: $FAILURES failures out of $TOTAL tests"
|
||||
echo ""
|
||||
|
||||
if [ "$FAILURES" -eq 0 ]; then
|
||||
echo "✅ All tests passing!"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Failed examples:"
|
||||
echo ""
|
||||
|
||||
jq -r '.examples[] | select(.status == "failed") | "\(.full_description)\n File: \(.file_path):\(.line_number)\n Error: \(.exception.class)\n Message: \(.exception.message)\n"' tmp/rspec_results.json
|
||||
|
||||
echo ""
|
||||
echo "Common fixes:"
|
||||
echo " - Check for typos in method names"
|
||||
echo " - Verify test data setup (factories, fixtures)"
|
||||
echo " - Check for nil objects"
|
||||
echo " - Review recent code changes"
|
||||
echo ""
|
||||
echo "To debug specific test:"
|
||||
echo " bundle exec rspec <file_path>:<line_number> --format documentation"
|
||||
65
skills/ruby-test-analyzer/scripts/run_tests.sh
Executable file
65
skills/ruby-test-analyzer/scripts/run_tests.sh
Executable file
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env bash
|
||||
# Run RSpec tests with intelligent filtering
|
||||
|
||||
set -e
|
||||
|
||||
MODE=${1:-all}
|
||||
|
||||
echo "🧪 Running RSpec tests (mode: $MODE)..."
|
||||
|
||||
# Check if RSpec is available
|
||||
if ! bundle exec rspec --version &> /dev/null; then
|
||||
echo "❌ RSpec not found. Add to Gemfile:"
|
||||
echo " gem 'rspec', '~> 3.12'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$MODE" in
|
||||
all)
|
||||
echo "Running all tests..."
|
||||
bundle exec rspec --format documentation
|
||||
;;
|
||||
fast)
|
||||
echo "Running tests without slow examples..."
|
||||
bundle exec rspec --tag ~slow --format progress
|
||||
;;
|
||||
failures)
|
||||
echo "Running only previously failed tests..."
|
||||
bundle exec rspec --only-failures --format documentation
|
||||
;;
|
||||
changed)
|
||||
echo "Running tests for changed files..."
|
||||
CHANGED_FILES=$(git diff --name-only --diff-filter=AM | grep '\.rb$' | grep -v '_spec\.rb$' || true)
|
||||
if [ -z "$CHANGED_FILES" ]; then
|
||||
echo "No changed Ruby files found"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
for file in $CHANGED_FILES; do
|
||||
# Convert lib/foo/bar.rb to spec/foo/bar_spec.rb
|
||||
spec_file=$(echo "$file" | sed 's/^lib/spec/;s/\.rb$/_spec.rb/')
|
||||
if [ -f "$spec_file" ]; then
|
||||
echo "Testing: $spec_file"
|
||||
bundle exec rspec "$spec_file" --format documentation
|
||||
fi
|
||||
done
|
||||
;;
|
||||
profile)
|
||||
echo "Running tests with profiling..."
|
||||
bundle exec rspec --profile 10 --format documentation
|
||||
;;
|
||||
*)
|
||||
# Treat as file path
|
||||
if [ -f "$MODE" ]; then
|
||||
echo "Running test file: $MODE"
|
||||
bundle exec rspec "$MODE" --format documentation
|
||||
else
|
||||
echo "Unknown mode: $MODE"
|
||||
echo "Available modes: all, fast, failures, changed, profile, <file_path>"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
echo ""
|
||||
echo "✅ Test run complete"
|
||||
Reference in New Issue
Block a user