Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:30:07 +08:00
commit d6f6fcbaad
33 changed files with 4697 additions and 0 deletions

View 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

View 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)

View 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"

View 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"