{ "dimension": "testing", "prefix": "TEST", "description": "Rules for detecting testing issues including coverage gaps, test quality, and mock usage", "rules": [ { "id": "missing-assertion", "category": "test-quality", "severity": "high", "pattern": "(?:it|test)\\s*\\([^)]+,\\s*(?:async\\s*)?\\(\\)\\s*=>\\s*\\{[^}]*\\}\\s*\\)", "patternType": "regex", "negativePatterns": ["expect", "assert", "should", "toBe", "toEqual"], "description": "Test case without assertions always passes and provides no value", "recommendation": "Add assertions to verify expected behavior. Each test should have at least one meaningful assertion", "fixExample": "// Before\nit('should process data', async () => {\n await processData(input);\n});\n\n// After\nit('should process data', async () => {\n const result = await processData(input);\n expect(result.success).toBe(true);\n expect(result.data).toHaveLength(3);\n});" }, { "id": "hardcoded-test-data", "category": "test-maintainability", "severity": "low", "pattern": "expect\\s*\\([^)]+\\)\\.toBe\\s*\\(['\"][^'\"]{20,}['\"]\\)", "patternType": "regex", "description": "Long hardcoded strings in assertions are brittle and hard to maintain", "recommendation": "Use snapshots for large outputs, or extract expected values to test fixtures", "fixExample": "// Before\nexpect(result).toBe('very long expected string that is hard to maintain...');\n\n// After\nexpect(result).toMatchSnapshot();\n// or\nconst expected = loadFixture('expected-output.json');\nexpect(result).toEqual(expected);" }, { "id": "no-error-test", "category": "coverage-gap", "severity": "medium", "pattern": "describe\\s*\\([^)]+", "patternType": "regex", "negativePatterns": ["throw", "reject", "error", "fail", "catch"], "description": "Test suite may be missing error path testing. Error handling is critical for reliability", "recommendation": "Add tests for error cases: invalid input, network failures, edge cases", "fixExample": "// Add error path tests\nit('should throw on invalid input', () => {\n expect(() => processData(null)).toThrow('Invalid input');\n});\n\nit('should handle network failure', async () => {\n mockApi.mockRejectedValue(new Error('Network error'));\n await expect(fetchData()).rejects.toThrow('Network error');\n});" }, { "id": "test-implementation-detail", "category": "test-quality", "severity": "medium", "pattern": "toHaveBeenCalledWith|toHaveBeenCalledTimes", "patternType": "includes", "description": "Testing implementation details (call counts, exact parameters) makes tests brittle to refactoring", "recommendation": "Prefer testing observable behavior and outcomes over internal implementation", "fixExample": "// Before - brittle\nexpect(mockService.process).toHaveBeenCalledTimes(3);\nexpect(mockService.process).toHaveBeenCalledWith('exact-arg');\n\n// After - behavior-focused\nexpect(result.items).toHaveLength(3);\nexpect(result.processed).toBe(true);" }, { "id": "skip-test", "category": "test-coverage", "severity": "high", "pattern": "it\\.skip|test\\.skip|xit|xdescribe|describe\\.skip", "patternType": "regex", "description": "Skipped tests indicate untested code paths or broken functionality", "recommendation": "Fix or remove skipped tests. If temporarily skipped, add TODO comment with issue reference", "fixExample": "// Before\nit.skip('should handle edge case', () => { ... });\n\n// After - either fix it\nit('should handle edge case', () => {\n // fixed implementation\n});\n\n// Or document why skipped\n// TODO(#123): Re-enable after API migration\nit.skip('should handle edge case', () => { ... });" } ] }