Claude-skill-registry local-service-testing
Use when code changes touch database, cache, queue, or other service-dependent components - enforces testing against real local services instead of mocks
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/local-service-testing" ~/.claude/skills/majiayu000-claude-skill-registry-local-service-testing-2ecd75 && rm -rf "$T"
skills/data/local-service-testing/SKILL.mdLocal Service Testing
Overview
Test against real services locally before pushing. CI validates—it doesn't discover.
Core principle: If you mock what you can run locally, you're hiding bugs.
Announce at start: "I'm using local-service-testing to verify changes against real services."
The Iron Law
CI DISCOVERS NOTHING. IF CI FINDS A BUG, YOUR LOCAL TESTING FAILED.
Local services exist for a reason. Use them.
Critical Clarification
Unit tests with mocks: REQUIRED (for TDD cycle) Integration tests with real services: ALSO REQUIRED
This skill does NOT replace mocking in unit tests. It ADDS the requirement for integration tests against real services. Both are mandatory.
When This Skill Applies
| Code Change | Required Service | Must Test Against |
|---|---|---|
| Database models/entities | postgres | Real postgres |
| Migrations | postgres | Real postgres |
| Repository/ORM layer | postgres | Real postgres |
| SQL queries | postgres | Real postgres |
| Cache operations | redis | Real redis |
| Session storage | redis | Real redis |
| Pub/sub messages | redis/rabbitmq | Real queue |
| Queue workers | redis/rabbitmq | Real queue |
| API endpoints | all services | All running |
If your change touches any of these, you MUST test against the real service.
Service Detection
At session start, the
session-start.sh hook reports available services:
Checking development services... ✓ Found docker-compose.yml Available services: ✓ postgres (running) ○ redis (not running) Tip: Start services with: docker-compose up -d
Starting Services
# Start all services docker-compose up -d # Start specific service docker-compose up -d postgres # Check status docker-compose ps
Service Connection Strings
| Service | Default Connection |
|---|---|
| postgres | |
| redis | |
| rabbitmq | |
Check your project's
.env.example or docker-compose.yml for actual values.
Testing Protocol
Step 1: Identify Service Dependencies
Before testing, identify which services your changes require:
# Check what files you've changed git diff --name-only HEAD~1 # Map to services: # *.sql, *migration*, *model*, *entity*, *repository* → postgres # *cache*, *redis*, *session*, *queue*, *pub*, *sub* → redis # *worker*, *job*, *consumer* → queue service
Step 2: Ensure Services Running
# Start required services docker-compose up -d postgres redis # Verify they're ready docker-compose ps # Test connectivity # Postgres psql postgresql://localhost:5432/dev -c "SELECT 1" # Redis redis-cli ping
Step 3: Run Integration Tests
# Run integration tests (not unit tests with mocks) pnpm test:integration # Or run specific integration test suite pnpm test --grep "integration" # For Python projects pytest tests/integration/ # For Go projects go test -tags=integration ./...
Step 4: Verify Locally Before Pushing
Before
git push:
# Full verification pnpm build pnpm lint pnpm typecheck pnpm test # Unit tests pnpm test:integration # Integration tests against real services
Two-Layer Testing Requirement
Both Are Required
| Test Layer | Purpose | Uses Mocks? | Uses Real Services? | Required? |
|---|---|---|---|---|
| Unit tests | TDD cycle, verify logic | YES | No | YES |
| Integration tests | Verify real behavior | No | YES | YES |
Why Both?
- Unit tests with mocks: Fast, enable RED-GREEN-REFACTOR, isolate logic
- Integration tests with services: Catch real-world failures mocks miss
We've experienced 80% failure rates with ORM migrations because unit tests with mocks passed but real databases rejected the changes.
What Mocks Miss
// Mock says this works const mockDb = { query: jest.fn().mockResolvedValue([{ id: 1 }]) }; // But real postgres throws: // ERROR: relation "users" does not exist // ERROR: column "email" cannot be null // ERROR: duplicate key violates unique constraint
Real services reveal:
- Schema mismatches
- Constraint violations
- Connection issues
- Transaction behavior
- Performance problems
Artifact Requirement
Before creating a PR, you must post local testing evidence to the issue.
Required Artifact Format
<!-- LOCAL-TESTING:START --> ## Local Service Testing | Service | Status | Verification | |---------|--------|--------------| | postgres | ✅ Running | Migrations applied, queries executed | | redis | ✅ Running | Cache operations verified | **Tests Run:** - `pnpm test:integration` - PASSED - Manual verification of [specific feature] **Tested At:** 2025-01-15T10:30:00Z <!-- LOCAL-TESTING:END -->
Where to Post
Post as a comment on the GitHub issue you're working on. This is checked by the
validate-local-testing.sh PreToolUse hook before PR creation.
When Artifact is Required
The hook checks:
- Does docker-compose.yml exist?
- Do changed files match service patterns?
- If yes to both → artifact required
If no services are relevant to your changes, no artifact is needed.
Common Patterns
Database Testing
// GOOD: Test against real postgres describe('UserRepository (integration)', () => { beforeAll(async () => { await db.migrate.latest(); }); afterAll(async () => { await db.destroy(); }); it('creates user with unique email constraint', async () => { await userRepo.create({ email: 'test@example.com' }); // Real postgres will throw on duplicate await expect( userRepo.create({ email: 'test@example.com' }) ).rejects.toThrow(/unique constraint/); }); });
Cache Testing
// GOOD: Test against real redis describe('CacheService (integration)', () => { beforeEach(async () => { await redis.flushdb(); }); it('expires keys after TTL', async () => { await cache.set('key', 'value', { ttl: 1 }); expect(await cache.get('key')).toBe('value'); await sleep(1100); expect(await cache.get('key')).toBeNull(); }); });
API Testing
// GOOD: Test against real services describe('POST /users (integration)', () => { it('creates user and caches result', async () => { const response = await request(app) .post('/users') .send({ email: 'new@example.com' }); expect(response.status).toBe(201); // Verify in real database const user = await db('users').where({ email: 'new@example.com' }).first(); expect(user).toBeDefined(); // Verify in real cache const cached = await redis.get(`user:${user.id}`); expect(cached).toBeDefined(); }); });
Troubleshooting
| Problem | Solution |
|---|---|
| Service not starting | Check |
| Connection refused | Ensure service is running and port is correct |
| Database doesn't exist | Run migrations: |
| Tests pass locally, fail in CI | Environment variable mismatch—check vs CI config |
| Flaky integration tests | Check for proper test isolation and cleanup |
Checklist
Before creating PR:
- Identified all service dependencies for changes
- All required services are running locally
- Integration tests pass against real services
- Posted local testing artifact to issue
- Did not rely on mocks for service-dependent code
Integration
This skill is called by:
- For integration testing requirementstdd-full-coverage
- Before PR creationissue-driven-development
- As a merge gateverification-before-merge
This skill is enforced by:
- PreToolUse hook blocks PR without artifactvalidate-local-testing.sh
This skill references:
- For service startup patternsenvironment-bootstrap
- For service detection at session startsession-start