From hyperpowers
Use to audit test quality with Google Fellow SRE scrutiny - identifies tautological tests, coverage gaming, weak assertions, missing corner cases. Creates br epic with tasks for improvements, then runs SRE task refinement on each.
How this skill is triggered — by the user, by Claude, or both
Slash command
/hyperpowers:analyzing-test-effectivenessThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
<skill_overview>
<skill_overview> Audit test suites for real effectiveness, not vanity metrics. Identify tests that provide false confidence (tautological, mock-testing, line hitters) and missing corner cases. Create br epic with tracked tasks for improvements. Run SRE task refinement on each task before execution.
CRITICAL MINDSET: Assume tests were written by junior engineers optimizing for coverage metrics. Default to skeptical—a test is RED or YELLOW until proven GREEN. You MUST read production code before categorizing tests. GREEN is the exception, not the rule. </skill_overview>
<rigidity_level> MEDIUM FREEDOM - Follow the 5-phase analysis process exactly. Categorization criteria (RED/YELLOW/GREEN) are rigid. Corner case discovery adapts to the specific codebase. Output format is flexible but must include all sections. </rigidity_level>
<quick_reference>
| Phase | Action | Output |
|---|---|---|
| 1. Inventory | List all test files and functions | Test catalog |
| 2. Read Production Code | Read the actual code each test claims to test | Context for analysis |
| 3. Trace Call Paths | Verify tests exercise production, not mocks/utilities | Call path verification |
| 4. Categorize (Skeptical) | Apply RED/YELLOW/GREEN - default to harsher rating | Categorized tests |
| 5. Self-Review | Challenge every GREEN - would a senior SRE agree? | Validated categories |
| 6. Corner Cases | Identify missing edge cases per module | Gap analysis |
| 7. Prioritize | Rank by business criticality | Priority matrix |
| 8. br Issues | Create epic + tasks, run SRE refinement | Tracked improvement plan |
MANDATORY: Read production code BEFORE categorizing tests. You cannot assess a test without understanding what it claims to test.
Core Questions for Each Test:
!= nil or testing fixtures → weak)br Integration (MANDATORY):
Mutation Testing Validation:
mvn org.pitest:pitest-maven:mutationCoverage)npx stryker run)mutmut run)
</quick_reference><when_to_use> Use this skill when:
Don't use when:
<the_process>
Announce: "I'm using hyperpowers:analyzing-test-effectiveness to audit test quality with Google Fellow SRE-level scrutiny."
Goal: Create complete catalog of tests to analyze.
# Find all test files (adapt pattern to language)
fd -e test.ts -e spec.ts -e _test.go -e Test.java -e test.py .
# Or use grep to find test functions
rg "func Test|it\(|test\(|def test_|@Test" --type-add 'test:*test*' -t test
# Count tests per module
for dir in src/*/; do
count=$(rg -c "func Test|it\(" "$dir" 2>/dev/null | wc -l)
echo "$dir: $count tests"
done
Create inventory TodoWrite:
- Analyze tests in src/auth/
- Analyze tests in src/api/
- Analyze tests in src/parser/
[... one per module]
MANDATORY: Before categorizing ANY test, you MUST:
Why this matters: Junior engineers commonly:
If you haven't read production code, you WILL miscategorize tests as GREEN when they're YELLOW or RED.
Assume every test is RED or YELLOW until you have concrete evidence it's GREEN.
For each test, apply these criteria:
2.1 Tautological Tests (pass by definition)
// ❌ RED: Verifies non-optional return is not nil
test('builder returns value', () => {
const result = new Builder().build();
expect(result).not.toBeNull(); // Always passes - return type guarantees this
});
// ❌ RED: Verifies enum has cases (compiler checks this)
test('status enum has values', () => {
expect(Object.values(Status).length).toBeGreaterThan(0);
});
// ❌ RED: Duplicates implementation
test('add returns sum', () => {
expect(add(2, 3)).toBe(2 + 3); // Tautology: testing 2+3 == 2+3
});
2.2 Mock-Testing Tests (test the mock, not production)
// ❌ RED: Only verifies mock was called, not actual behavior
test('service fetches data', () => {
const mockApi = { fetch: jest.fn().mockResolvedValue({ data: [] }) };
const service = new Service(mockApi);
service.getData();
expect(mockApi.fetch).toHaveBeenCalled(); // Tests mock, not service logic
});
// ❌ RED: Mock determines test outcome
test('processor handles data', () => {
const mockParser = { parse: jest.fn().mockReturnValue({ valid: true }) };
const result = processor.process(mockParser);
expect(result.valid).toBe(true); // Just returns what mock returns
});
2.3 Line Hitters (execute without asserting)
// ❌ RED: Calls function, doesn't verify outcome
test('processor runs', () => {
const processor = new Processor();
processor.run(); // No assertion - just verifies no crash
});
// ❌ RED: Assertion is trivial
test('config loads', () => {
const config = loadConfig();
expect(config).toBeDefined(); // Too weak - doesn't verify correct values
});
2.4 Evergreen/Liar Tests (always pass)
// ❌ RED: Catches and ignores exceptions
test('parser handles input', () => {
try {
parser.parse(input);
expect(true).toBe(true); // Always passes
} catch (e) {
// Swallowed - test passes even on exception
}
});
// ❌ RED: Test setup bypasses code under test
test('validator validates', () => {
const validator = new Validator({ skipValidation: true }); // Oops
expect(validator.validate(badInput)).toBe(true);
});
2.5 Happy Path Only
// ⚠️ YELLOW: Only tests valid input
test('parse valid json', () => {
const result = parse('{"name": "test"}');
expect(result.name).toBe('test');
});
// Missing: empty string, malformed JSON, deeply nested, unicode, huge payload
2.6 Weak Assertions
// ⚠️ YELLOW: Assertion too weak
test('fetch returns data', () => {
const result = await fetch('/api/users');
expect(result).not.toBeNull(); // Should verify actual content
expect(result.length).toBeGreaterThan(0); // Should verify exact count or specific items
});
2.7 Partial Coverage
// ⚠️ YELLOW: Tests success, not failure
test('create user succeeds', () => {
const user = createUser({ name: 'test', email: '[email protected]' });
expect(user.id).toBeDefined();
});
// Missing: duplicate email, invalid email, missing fields, database error
GREEN is the EXCEPTION, not the rule. A test is GREEN only if ALL of the following are true:
!= nil or > 0Before marking ANY test GREEN, you MUST state:
If you cannot fill in those blanks, the test is YELLOW at best.
3.1 Behavior Verification (Must exercise PRODUCTION code)
// ✅ GREEN: Verifies specific behavior with exact values FROM PRODUCTION
test('calculateTotal applies discount correctly', () => {
const cart = new Cart([{ price: 100, quantity: 2 }]); // Real Cart class
cart.applyDiscount('SAVE20'); // Real discount logic
expect(cart.total).toBe(160); // 200 - 20% = 160
});
// GREEN because: Exercises Cart.applyDiscount production code
// Would catch: Discount calculation bugs, rounding errors
// Assertion: Verifies exact computed value from production
3.2 Edge Case Coverage (Must test PRODUCTION paths)
// ✅ GREEN: Tests boundary conditions IN PRODUCTION CODE
test('username rejects empty string', () => {
expect(() => new User({ username: '' })).toThrow(ValidationError);
});
// GREEN because: Exercises User constructor validation (production)
// Would catch: Missing empty string validation
// Assertion: Exact error type from production code
test('username handles unicode', () => {
const user = new User({ username: '日本語ユーザー' });
expect(user.username).toBe('日本語ユーザー');
});
// GREEN because: Exercises User constructor and storage (production)
// Would catch: Unicode corruption, encoding bugs
// Assertion: Exact value preserved through production code
3.3 Error Path Testing (Must verify PRODUCTION errors)
// ✅ GREEN: Verifies error handling IN PRODUCTION CODE
test('fetch returns specific error on 404', () => {
mockServer.get('/api/user/999').reply(404); // External mock OK
await expect(fetchUser(999)).rejects.toThrow(UserNotFoundError);
});
// GREEN because: Exercises fetchUser error handling (production)
// Would catch: Wrong error type, swallowed errors
// Assertion: Exact error type from production code
CAUTION: A test that uses mocks for EXTERNAL dependencies (APIs, databases) can still be GREEN if it exercises PRODUCTION logic. A test that mocks the code under test is RED.
Before finalizing ANY categorization, complete this checklist:
If you have ANY doubt about a GREEN, downgrade to YELLOW. If you have ANY doubt about a YELLOW, consider RED.
Common mistakes that cause false GREENs:
MANDATORY: For every RED or YELLOW classification, provide detailed justification.
This forces you to verify your classification is correct by explaining exactly WHY the test is problematic.
### [Test Name] - RED/YELLOW
**Test code (file:lines):**
- Line X: `code` - [what this line does]
- Line Y: `code` - [what this line does]
- Line Z: `assertion` - [what this asserts]
**Production code it claims to test (file:lines):**
- [Brief description of what production code does]
**Why RED/YELLOW:**
- [Specific reason with line references]
- [What bug could slip through despite this test passing]
### testAuthWorks - RED (Tautological)
**Test code (auth_test.ts:45-52):**
- Line 46: `const auth = new AuthService()` - Creates auth instance
- Line 47: `const result = auth.login('user', 'pass')` - Calls login
- Line 48: `expect(result).not.toBeNull()` - Asserts result exists
**Production code (auth.ts:78-95):**
- login() returns AuthResult object (never null by TypeScript types)
**Why RED:**
- Line 48 asserts `!= null` but TypeScript guarantees non-null return
- If login returned {success: false, error: "invalid"}, test still passes
- Bug example: Wrong password accepted → returns {success: true} → test passes
### testParseJson - YELLOW (Weak Assertion)
**Test code (parser_test.ts:23-30):**
- Line 24: `const input = '{"name": "test"}'` - Valid JSON input
- Line 25: `const result = parse(input)` - Calls production parser
- Line 26: `expect(result).toBeDefined()` - Asserts result exists
- Line 27: `expect(result.name).toBe('test')` - Verifies one field
**Production code (parser.ts:12-45):**
- parse() handles JSON parsing with error handling and validation
**Why YELLOW:**
- Line 26-27 only test happy path with valid input
- Missing: malformed JSON, empty string, deeply nested, unicode
- Bug example: parse('') throws unhandled exception → not caught by test
- Upgrade path: Add edge case inputs with specific error assertions
Writing the justification FORCES you to:
If you cannot write this justification, you haven't done the analysis properly.
For each module, identify missing corner case tests:
| Category | Examples | Tests to Add |
|---|---|---|
| Empty values | "", [], {}, null | test_empty_X_rejected/handled |
| Boundary values | 0, -1, MAX_INT, MAX_LEN | test_boundary_X_handled |
| Unicode | RTL, emoji, combining chars, null byte | test_unicode_X_preserved |
| Injection | SQL: '; DROP, XSS: <script>, cmd: ; rm | test_injection_X_escaped |
| Malformed | truncated JSON, invalid UTF-8, wrong type | test_malformed_X_error |
| Category | Examples | Tests to Add |
|---|---|---|
| Uninitialized | Use before init, double init | test_uninitialized_X_error |
| Already closed | Use after close, double close | test_closed_X_error |
| Concurrent | Parallel writes, read during write | test_concurrent_X_safe |
| Re-entrant | Callback calls same method | test_reentrant_X_safe |
| Category | Examples | Tests to Add |
|---|---|---|
| Network | timeout, connection refused, DNS fail | test_network_X_timeout |
| Partial response | truncated, corrupted, slow | test_partial_response_handled |
| Rate limiting | 429, quota exceeded | test_rate_limit_handled |
| Service errors | 500, 503, malformed response | test_service_error_handled |
| Category | Examples | Tests to Add |
|---|---|---|
| Exhaustion | OOM, disk full, max connections | test_resource_X_graceful |
| Contention | file locked, resource busy | test_contention_X_handled |
| Permissions | access denied, read-only | test_permission_X_error |
For each module, create corner case checklist:
### Module: src/auth/
**Covered Corner Cases:**
- [x] Empty password rejected
- [x] SQL injection in username escaped
**Missing Corner Cases (MUST ADD):**
- [ ] Unicode username preserved after roundtrip
- [ ] Concurrent login attempts don't corrupt session
- [ ] Password with null byte handled
- [ ] Very long password (10KB) rejected gracefully
- [ ] Login rate limiting enforced
**Priority:** HIGH (auth is business-critical)
| Priority | Criteria | Action Timeline |
|---|---|---|
| P0 - Critical | Auth, payments, data integrity | This sprint |
| P1 - High | Core business logic, user-facing features | Next sprint |
| P2 - Medium | Internal tools, admin features | Backlog |
| P3 - Low | Utilities, non-critical paths | As time permits |
Rank modules:
1. P0: src/auth/ - 5 RED tests, 12 missing corner cases
2. P0: src/payments/ - 2 RED tests, 8 missing corner cases
3. P1: src/api/ - 8 RED tests, 15 missing corner cases
4. P2: src/admin/ - 3 RED tests, 6 missing corner cases
CRITICAL: All findings MUST be tracked in br and go through SRE task refinement.
Create a br epic for test quality improvement, then one task per finding category. Each task must include: Goal, Tests to address (file:line), Success Criteria, Anti-patterns. Use common-patterns/br-commands.md for br command reference.
Tasks to create (in dependency order):
Link as children of the epic; set dependencies (remove → strengthen → add → validate).
MANDATORY after creating tasks: Run hyperpowers:sre-task-refinement on each task. Apply Category 8 (Test Meaningfulness) especially — verify proposed tests actually catch bugs.
# Test Effectiveness Analysis: [Project Name]
## Executive Summary
| Metric | Count | % |
|--------|-------|---|
| Total tests analyzed | N | 100% |
| RED (remove/replace) | N | X% |
| YELLOW (strengthen) | N | X% |
| GREEN (keep) | N | X% |
| Missing corner cases | N | - |
**Overall Assessment:** [CRITICAL / NEEDS WORK / ACCEPTABLE / GOOD]
## Detailed Findings
For each RED/YELLOW test: Test name, File:Line, Problem, Action.
For each missing corner case: Corner Case, Bug Risk, Recommended Test.
## br Issues Created
- br-N (epic): Test Quality Improvement
- br-N (P0): Remove tautological tests
- br-N (P1): Strengthen weak assertions
- br-N (P1): Add corner case tests
- br-N (P1): Validate with mutation testing
</the_process>
High coverage but production bugs keep appearing
# Test suite stats
Coverage: 92%
Tests: 245 passing
Yet production issues:
- Auth bypass via empty password
- Data corruption on concurrent updates
Crash on unicode usernames
<why_it_fails>
Phase 1 - Inventory:
fd -e test.ts src/
# Found: auth.test.ts, user.test.ts, data.test.ts
Phase 2 - Categorize:
### auth.test.ts
| Test | Category | Problem |
|------|----------|---------|
| testAuthWorks | RED | Only checks `!= null` |
| testLoginFlow | YELLOW | Happy path only, no empty password |
| testTokenExpiry | GREEN | Verifies exact error |
### data.test.ts
| Test | Category | Problem |
|------|----------|---------|
| testDataSaves | RED | No assertion, just calls save() |
| testConcurrentWrites | MISSING | Not tested at all |
Phase 3 - Corner cases:
### auth module (P0)
Missing:
- [ ] test_empty_password_rejected
- [ ] test_unicode_username_preserved
- [ ] test_concurrent_login_safe
Phase 5 - Plan:
### Immediate
- Remove testAuthWorks (tautological)
- Remove testDataSaves (line hitter)
### This Sprint
- Add test_empty_password_rejected
- Add test_concurrent_writes_safe
- Strengthen testLoginFlow with edge cases
Result: Production bugs prevented by meaningful tests.
Mock-heavy test suite that breaks on every refactor
# Every refactor breaks 50+ tests
# But bugs slip through to production
test('service processes data', () => {
const mockDb = jest.fn().mockReturnValue({ data: [] });
const mockCache = jest.fn().mockReturnValue(null);
const mockLogger = jest.fn();
const mockValidator = jest.fn().mockReturnValue(true);
const service = new Service(mockDb, mockCache, mockLogger, mockValidator);
service.process({ id: 1 });
expect(mockDb).toHaveBeenCalled();
expect(mockValidator).toHaveBeenCalled();
// Tests mock wiring, not actual behavior
});
<why_it_fails>
### service.test.ts
| Test | Category | Problem | Action |
|------|----------|---------|--------|
| testServiceProcesses | RED | Only verifies mocks called | Replace with integration test |
| testServiceValidates | RED | Mock determines outcome | Test real validator |
| testServiceCaches | RED | Tests mock cache | Use real cache with test data |
Replacement strategy:
// ❌ Before: Tests mock wiring
test('service validates', () => {
const mockValidator = jest.fn().mockReturnValue(true);
const service = new Service(mockValidator);
expect(mockValidator).toHaveBeenCalled();
});
// ✅ After: Tests real behavior
test('service rejects invalid data', () => {
const service = new Service(new RealValidator());
const result = service.process({ id: -1 }); // Invalid ID
expect(result.error).toBe('INVALID_ID');
});
test('service accepts valid data', () => {
const service = new Service(new RealValidator());
const result = service.process({ id: 1, name: 'test' });
expect(result.success).toBe(true);
expect(result.data.name).toBe('test');
});
Result: Tests verify behavior, not implementation. Refactoring doesn't break tests. Real bugs caught.
<critical_rules>
You WILL be tempted to:
A false GREEN is worse than a false YELLOW. When in doubt, be harsher.
All of these mean: STOP. The test is probably RED or YELLOW.
<verification_checklist> Before completing analysis:
Analysis Quality (MANDATORY):
Per module:
Overall:
br Integration (MANDATORY):
SRE Refinement Verification:
Validation:
This skill creates:
Workflow chain:
analyzing-test-effectiveness
↓ (creates br issues)
sre-task-refinement (on each task)
↓ (refines tasks)
executing-plans (implements tasks)
↓ (runs validation)
review-implementation (verifies quality)
This skill informs:
Mutation testing tools:
mvn org.pitest:pitest-maven:mutationCoverage)npx stryker run)mutmut run)dotnet stryker)
Key insight from Google: "Coverage mainly tells you about code that has no tests: it doesn't tell you about the quality of testing for the code that's 'covered'."
When stuck:
npx claudepluginhub sqve/hyperpowersCreates structured, bite-sized implementation plans from specs or requirements before writing code. Useful for breaking down multi-step tasks into testable steps with file structure and task boundaries.