Use when code changes touch database, cache, queue, or other service-dependent components - enforces testing against real local services instead of mocks
When code changes touch database, cache, queue, or other service-dependent components, this enforces testing against real local services instead of mocks. It requires integration tests to run against actual postgres, redis, or rabbitmq instances before pushing changes.
/plugin marketplace add troykelly/claude-skills/plugin install issue-driven-development@troykelly-skillsThis skill is limited to using the following tools:
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."
CI DISCOVERS NOTHING.
IF CI FINDS A BUG, YOUR LOCAL TESTING FAILED.
Local services exist for a reason. Use them.
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.
| 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.
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
# Start all services
docker-compose up -d
# Start specific service
docker-compose up -d postgres
# Check status
docker-compose ps
| Service | Default Connection |
|---|---|
| postgres | postgresql://localhost:5432/dev |
| redis | redis://localhost:6379 |
| rabbitmq | amqp://localhost:5672 |
Check your project's .env.example or docker-compose.yml for actual values.
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
# 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
# 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 ./...
Before git push:
# Full verification
pnpm build
pnpm lint
pnpm typecheck
pnpm test # Unit tests
pnpm test:integration # Integration tests against real services
| 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 |
We've experienced 80% failure rates with ORM migrations because unit tests with mocks passed but real databases rejected the changes.
// 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:
Before creating a PR, you must post local testing evidence to the issue.
<!-- 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 -->
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.
The hook checks:
If no services are relevant to your changes, no artifact is needed.
// 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/);
});
});
// 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();
});
});
// 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();
});
});
| Problem | Solution |
|---|---|
| Service not starting | Check docker-compose logs [service] |
| Connection refused | Ensure service is running and port is correct |
| Database doesn't exist | Run migrations: pnpm migrate |
| Tests pass locally, fail in CI | Environment variable mismatch—check .env vs CI config |
| Flaky integration tests | Check for proper test isolation and cleanup |
Before creating PR:
This skill is called by:
tdd-full-coverage - For integration testing requirementsissue-driven-development - Before PR creationverification-before-merge - As a merge gateThis skill is enforced by:
validate-local-testing.sh - PreToolUse hook blocks PR without artifactThis skill references:
environment-bootstrap - For service startup patternssession-start - For service detection at session startThis skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.