Analyze Go test files (*_test.go) for quality issues, anti-patterns, and code smells...
Analyze Go test files to identify quality issues, anti-patterns, and code smells that make tests flaky, slow, complex, or unmaintainable. Provide actionable refactoring suggestions to improve test reliability and clarity.
Test quality encompasses:
Use this skill when users ask to:
This skill provides four Python scripts that analyze Go test files and output structured JSON. Each script focuses on a specific category of test quality issues.
Script: check-external-deps.py
Look for patterns indicating real external dependencies:
sql.Open, gorm.DB, connection strings)http.Get, http.Post, http.Client{})ListenAndServe, http.Server{})os.Create, os.Open without t.TempDir())time.Sleep - indicates flaky timing-based tests)Why critical: External dependencies make tests slow, flaky, and environment-dependent. Tests fail in CI, cannot run offline, and cannot run in parallel safely.
Common fixes: Use mocks/fakes, httptest.Server for HTTP testing, test containers for integration tests, t.TempDir() for file operations, and replace time.Sleep with channels, WaitGroups, or require.Eventually.
See references/pattern-details.md for detailed pattern descriptions and fix examples.
Script: check-complexity.py
Analyze test structure for complexity indicators:
for, if, switch statements in tests)Why it matters: Complex tests are hard to understand, maintain, and debug. Excessive mocking indicates coupling to implementation. Generic names provide no documentation value.
Common fixes: Extract setup to table-driven test helpers, reduce mocking by using real objects when simple, split complex tests into focused tests, use descriptive test names.
See references/pattern-details.md for detailed guidance and refactoring examples.
Script: check-flaky-patterns.py
Detect patterns causing non-deterministic test failures:
time.Sleep() calls (timing-based synchronization)go func() without WaitGroup/channels)context.WithTimeout with fixed durations)rand. without seeded source)time.Now() without mocking)t.Parallel() but don't)Why critical: Flaky tests fail intermittently, undermining trust in the test suite. Timing-based synchronization breaks on slower CI machines. Race conditions cause unpredictable failures.
Common fixes: Use channels/WaitGroups for async operations, mock time with fixed values, use seeded random generators, add synchronization primitives, enable t.Parallel() for independent tests.
See references/pattern-details.md for comprehensive flaky pattern examples.
Script: check-anti-patterns.py
Identify testing anti-patterns:
reflect., FieldByName, unsafe.Pointer)EXPECT/ASSERT calls per test)assert.Equal without descriptive messages)os.Setenv without defer or t.Setenv)Why it matters: Testing unexported internals couples tests to implementation. Over-verifying mocks tests mock behavior, not actual behavior. Missing messages make failures hard to diagnose.
Common fixes: Test public API only, verify behavior/outcomes not mock sequences, add descriptive assertion messages, split tests with multiple assertions, use t.Setenv and t.Cleanup.
See references/pattern-details.md for anti-pattern details and solutions.
Follow these steps when analyzing Go test quality:
Confirm this is a Go project with test files:
# Check for go.mod
if [ ! -f "go.mod" ]; then
echo "Error: Not a Go project (no go.mod found)"
exit 1
fi
# Find test files
test_files=$(fd -e go -g '*_test.go' . 2>/dev/null || find . -name '*_test.go' 2>/dev/null)
if [ -z "$test_files" ]; then
echo "No Go test files (*_test.go) found in this project"
exit 0
fi
test_count=$(echo "$test_files" | wc -l)
echo "Found $test_count test files to analyze"
Execute all four scripts to gather comprehensive quality data. Run in parallel for speed:
# Parallel execution
uv run ${CLAUDE_SKILL_ROOT}/scripts/check-external-deps.py . > /tmp/external-deps.json &
PID1=$!
uv run ${CLAUDE_SKILL_ROOT}/scripts/check-complexity.py . > /tmp/complexity.json &
PID2=$!
uv run ${CLAUDE_SKILL_ROOT}/scripts/check-flaky-patterns.py . > /tmp/flaky.json &
PID3=$!
uv run ${CLAUDE_SKILL_ROOT}/scripts/check-anti-patterns.py . > /tmp/anti-patterns.json &
PID4=$!
# Wait for all to complete
wait $PID1 $PID2 $PID3 $PID4
Alternatively, run sequentially:
uv run ${CLAUDE_SKILL_ROOT}/scripts/check-external-deps.py .
uv run ${CLAUDE_SKILL_ROOT}/scripts/check-complexity.py .
uv run ${CLAUDE_SKILL_ROOT}/scripts/check-flaky-patterns.py .
uv run ${CLAUDE_SKILL_ROOT}/scripts/check-anti-patterns.py .
Each script outputs JSON in a consistent format. Collect and parse results:
# Extract all issues from all scripts
jq -s '[.[].issues[]]' /tmp/*.json > /tmp/all-issues.json
# Count by severity
jq '[.[] | select(.severity == "Critical")] | length' /tmp/all-issues.json
See references/json-schema.md for complete JSON schema documentation and parsing examples.
Combine results from all scripts and organize for presentation:
time.Sleep detected by both external-deps and flaky-patterns)Deduplication logic:
# Remove duplicates (same file + line, keep highest severity)
jq 'unique_by([.file, .line])' /tmp/all-issues.json
Severity prioritization:
time.Sleep, race conditionsPresent findings in a clear, actionable format organized by severity. Include:
See references/report-examples.md for complete report templates and examples.
Report structure:
## Test Quality Analysis Report
**Summary:**
- Total issues: [count]
- Critical: [count]
- High: [count]
- Medium: [count]
- Files with issues: [count] / [total] ([percentage]%)
---
## Critical Issues
### [file]:[line] - [test_name] [category]
**Issue:** [description]
**Code:** [snippet]
**Impact:** [why it matters]
**Suggested fix:** [solution with code]
---
## Recommendations
1. **Priority 1 (Critical):** [action items]
2. **Priority 2 (High):** [action items]
3. **Priority 3 (Medium):** [action items]
User query:
"Check my Go tests for quality issues"
Expected workflow:
User query:
"Find flaky tests that might be failing intermittently"
Expected workflow:
check-flaky-patterns.py scripttime.Sleep, goroutines without sync, and non-deterministic patternsrequire.Eventually)Handle these error cases gracefully:
if [ ! -f "go.mod" ]; then
echo '{"error": "Not a Go project", "message": "No go.mod file found"}' | jq .
exit 0 # Not an error, just not applicable
fi
test_files=$(fd -g '*_test.go' . 2>/dev/null)
if [ -z "$test_files" ]; then
echo '{"error": "No tests found", "message": "No *_test.go files in project"}' | jq .
exit 0
fi
# Dependencies are automatically installed by uv
# If uv is not available, install it first:
if ! command -v uv &> /dev/null; then
echo '{"error": "Missing dependency", "message": "uv not installed. Install from https://docs.astral.sh/uv/"}' | jq .
exit 1
fi
If all scripts return zero issues, congratulate the user on having high-quality tests! Present a positive message highlighting good practices observed.
references/pattern-details.md - Comprehensive documentation of all detected patterns with examples and fixesreferences/json-schema.md - Complete JSON output format, field descriptions, and parsing examplesreferences/report-examples.md - Full example reports showing different scenarios and output formatsuv