Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Write automated tests for features, validate functionality against acceptance criteria, and ensure code coverage. Use when writing test code, verifying functionality, or adding test coverage to existing code.
.claude/skills/microck-testing-code/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-20 | ✗→✓ | ▲ Improved | 100% | 0% |
| case-21 | ✗→✓ | ▲ Improved | 55% | 0% |
| case-05 | ✓→✗ | ▼ Worse | 136% | 0% |
| case-06 | ✓→✓ | = Same ✓ | 121% | 0% |
| case-01 | ✓→✓ | = Same ✓ | 20% | 0% |
Test writing follows a systematic approach: determine scope, understand patterns, map to requirements, write tests, verify coverage.
Read project documentation:
docs/user-stories/US-###-*.md for acceptance criteria to testdocs/feature-spec/F-##-*.md for technical requirementsdocs/api-contracts.yaml for API specificationsChoose test types needed:
Investigate current test approach:
Use code-finder agents if unfamiliar with test structure.
Convert 3-5 acceptance criteria to specific test cases across test types:
Example mapping:
markdown## User Story: US-101 User Login ### Test Cases 1. **Unit: Authentication service** - validateCredentials() returns true for valid email/password - validateCredentials() returns false for invalid password - checkAccountStatus() detects locked accounts 2. **Integration: Login endpoint** - POST /api/login with valid creds returns 200 + token - POST /api/login with invalid creds returns 401 + error - POST /api/login with locked account returns 403 3. **Component: Login form** - Submitting form calls login API - Error message displays on 401 response - Success redirects to /dashboard 4. **E2E: Complete login flow** - User enters credentials → submits → sees dashboard - User enters wrong password → sees error → retries successfully
Unit Test Structure:
javascriptdescribe('AuthService', () => { describe('validateCredentials', () => { it('returns true for valid email and password', async () => { const result = await authService.validateCredentials( 'user@example.com', 'ValidPass123' ); expect(result).toBe(true); }); it('returns false for invalid password', async () => { const result = await authService.validateCredentials( 'user@example.com', 'WrongPassword' ); expect(result).toBe(false); }); }); });
Integration Test Structure:
javascriptdescribe('POST /api/auth/login', () => { beforeEach(async () => { await resetTestDatabase(); await createTestUser({ email: 'test@example.com', password: 'Test123!' }); }); it('returns 200 and token for valid credentials', async () => { const response = await request(app) .post('/api/auth/login') .send({ email: 'test@example.com', password: 'Test123!' }); expect(response.status).toBe(200); expect(response.body).toHaveProperty('token'); expect(response.body.token).toMatch(/^eyJ/); // JWT format }); it('returns 401 for invalid password', async () => { const response = await request(app) .post('/api/auth/login') .send({ email: 'test@example.com', password: 'WrongPassword' }); expect(response.status).toBe(401); expect(response.body.error).toBe('Invalid credentials'); }); });
Component Test Structure:
javascriptdescribe('LoginForm', () => { it('submits form with valid data', async () => { const mockLogin = jest.fn().mockResolvedValue({ success: true }); render(<LoginForm onLogin={mockLogin} />); await userEvent.type(screen.getByLabelText(/email/i), 'user@example.com'); await userEvent.type(screen.getByLabelText(/password/i), 'Password123'); await userEvent.click(screen.getByRole('button', { name: /log in/i })); expect(mockLogin).toHaveBeenCalledWith({ email: 'user@example.com', password: 'Password123' }); }); it('displays error message on API failure', async () => { const mockLogin = jest.fn().mockRejectedValue(new Error('Invalid credentials')); render(<LoginForm onLogin={mockLogin} />); await userEvent.type(screen.getByLabelText(/email/i), 'user@example.com'); await userEvent.type(screen.getByLabelText(/password/i), 'wrong'); await userEvent.click(screen.getByRole('button', { name: /log in/i })); expect(await screen.findByText(/invalid credentials/i)).toBeInTheDocument(); }); });
E2E Test Structure:
javascripttest('user can log in successfully', async ({ page }) => { await page.goto('/login'); await page.fill('[name="email"]', 'test@example.com'); await page.fill('[name="password"]', 'Test123!'); await page.click('button:has-text("Log In")'); await page.waitForURL('/dashboard'); expect(page.url()).toContain('/dashboard'); });
Include boundary conditions and error paths:
javascriptdescribe('Edge cases', () => { it('handles empty email gracefully', async () => { await expect( authService.validateCredentials('', 'password') ).rejects.toThrow('Email is required'); }); it('handles extremely long password', async () => { const longPassword = 'a'.repeat(10000); await expect( authService.validateCredentials('user@example.com', longPassword) ).rejects.toThrow('Password too long'); }); it('handles network timeout', async () => { jest.spyOn(global, 'fetch').mockImplementation( () => new Promise((resolve) => setTimeout(resolve, 10000)) ); await expect( authService.login('user@example.com', 'pass') ).rejects.toThrow('Request timeout'); }); });
Edge cases to always include:
Create reusable test fixtures:
javascript// tests/fixtures/users.ts export const validUser = { email: 'test@example.com', password: 'Test123!', name: 'Test User' }; export const invalidUsers = { noEmail: { password: 'Test123!' }, noPassword: { email: 'test@example.com' }, invalidEmail: { email: 'not-an-email', password: 'Test123!' }, weakPassword: { email: 'test@example.com', password: '123' } }; // Use in tests import { validUser, invalidUsers } from './fixtures/users'; it('validates user data', () => { expect(validate(validUser)).toBe(true); expect(validate(invalidUsers.noEmail)).toBe(false); });
When tests are independent (different modules, different test types), spawn parallel agents:
Pattern 1: Layer-based
Pattern 2: Feature-based
Pattern 3: Type-based
Execute test suite:
bash# Unit tests npm test -- --coverage # Integration tests npm run test:integration # E2E tests npm run test:e2e # All tests npm run test:all
Verify coverage:
Coverage:
Structure:
Data:
Integration:
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-06 | pass→pass | 11,042 | 9,271 | -16% | 1 | 1 | 0% | 1,753 | 3,875 | +121% | 0 | 0 | — |
case-01 | pass→pass | 29,659 | 23,164 | -22% | 1 | 1 | 0% | 6,199 | 7,429 | +20% | 0 | 0 | — |
case-02 | fail→fail | 24,436 | 18,963 | -22% | 1 | 1 | 0% | 5,547 | 6,488 | +17% | 0 | 0 | — |
case-03 | pass→pass | 8,509 | 17,800 | +109% | 1 | 1 | 0% | 1,750 | 6,336 | +262% | 0 | 0 | — |
case-04 | pass→pass | 6,625 | 8,539 | +29% | 1 | 1 | 0% | 1,418 | 4,207 | +197% | 0 | 0 | — |
case-05 | pass→fail | 10,522 | 13,661 | +30% | 1 | 1 | 0% | 2,345 | 5,540 | +136% | 0 | 0 | — |
case-07 | pass→pass | 5,688 | 4,916 | -14% | 1 | 1 | 0% | 985 | 3,215 | +226% | 0 | 0 | — |
case-08 | pass→pass | 9,285 | 5,903 | -36% | 1 | 1 | 0% | 1,495 | 3,169 | +112% | 0 | 0 | — |
case-09 | pass→pass | 5,232 | 7,742 | +48% | 1 | 1 | 0% | 865 | 3,673 | +325% | 0 | 0 | — |
case-10 | pass→pass | 10,397 | 16,885 | +62% | 1 | 1 | 0% | 2,073 | 5,297 | +156% | 0 | 0 | — |
case-11 | pass→pass | 12,325 | 9,864 | -20% | 1 | 1 | 0% | 2,484 | 4,461 | +80% | 0 | 0 | — |
case-12 | pass→pass | 13,463 | 10,621 | -21% | 1 | 1 | 0% | 2,516 | 4,297 | +71% | 0 | 0 | — |
case-13 | pass→pass | 11,872 | 8,467 | -29% | 1 | 1 | 0% | 2,340 | 3,960 | +69% | 0 | 0 | — |
case-14 | pass→pass | 9,391 | 14,251 | +52% | 1 | 1 | 0% | 2,070 | 5,064 | +145% | 0 | 0 | — |
case-15 | pass→pass | 9,754 | 9,445 | -3% | 1 | 1 | 0% | 1,853 | 4,153 | +124% | 0 | 0 | — |
case-16 | pass→pass | 16,867 | 16,871 | +0% | 1 | 1 | 0% | 2,943 | 5,549 | +89% | 0 | 0 | — |
case-17 | pass→pass | 17,933 | 11,966 | -33% | 1 | 1 | 0% | 3,019 | 4,633 | +53% | 0 | 0 | — |
case-18 | pass→pass | 14,989 | 11,290 | -25% | 1 | 1 | 0% | 2,383 | 4,247 | +78% | 0 | 0 | — |
case-19 | pass→pass | 15,349 | 10,343 | -33% | 1 | 1 | 0% | 2,451 | 4,099 | +67% | 0 | 0 | — |
case-20 | fail→pass | 11,882 | 8,141 | -31% | 1 | 1 | 0% | 1,888 | 3,771 | +100% | 0 | 0 | — |
case-21 | fail→pass | 9,201 | 3,163 | -66% | 1 | 1 | 0% | 1,772 | 2,748 | +55% | 0 | 0 | — |
case-22 | pass→pass | 7,177 | 5,223 | -27% | 1 | 1 | 0% | 1,171 | 3,114 | +166% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +5 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.