Claude Code Plugins

Community-maintained marketplace

Feedback

Test development with pytest, fixtures, and integration testing. Use for writing tests, test patterns, coverage, parametrization, and debugging test failures.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name testing
description Test development with pytest, fixtures, and integration testing. Use for writing tests, test patterns, coverage, parametrization, and debugging test failures.

Testing Skill

Philosophy: See .claude/rules/integration-testing.md (test real services, mock only external network APIs).

Running Tests

Test Type Location Command
Backend Docker docker compose exec backend pytest tests/
Frontend Docker docker compose exec frontend pnpm test
E2E Host cd playwright && pnpm test
# All quality gates
just check

# Backend with coverage
docker compose exec backend pytest tests/ --cov=app --cov-report=term-missing

# Specific test
docker compose exec backend pytest tests/test_signal_chains.py -v -k "test_create"

# Stop on first failure
docker compose exec backend pytest -x

# E2E specific test
cd playwright && pnpm test --grep "signal chain"

Test Structure

backend/tests/
├── conftest.py           # Shared fixtures
├── test_*.py             # Integration tests (real DB)

playwright/tests/
├── *.spec.ts             # E2E tests (real backend)
├── fixtures/             # Test helpers
└── global-setup.ts       # DB seeding

Fixtures (conftest.py)

@pytest.fixture
async def db_session(async_engine) -> AsyncSession:
    """Real database with transaction rollback."""
    async with async_engine.begin() as conn:
        session = AsyncSession(bind=conn)
        yield session
        await conn.rollback()

@pytest.fixture
async def authenticated_user(db_session: AsyncSession) -> dict:
    """Create real test user."""
    user = User(username="test-user", tone3000_id="test-123")
    db_session.add(user)
    await db_session.commit()
    token = create_access_token(user.id)
    return {"user": user, "token": token}

Test Patterns

Integration Test (Preferred)

@pytest.mark.asyncio
async def test_create_signal_chain(async_client: AsyncClient, authenticated_user: dict):
    response = await async_client.post(
        "/api/v1/signal-chains",
        json={"name": "Test Chain", "platform": "nam"},
        headers={"Authorization": f"Bearer {authenticated_user['token']}"},
    )
    assert response.status_code == 201
    assert response.json()["id"] is not None  # Real DB ID

Unit Test (Pure Logic Only)

def test_signal_chain_validates_name():
    with pytest.raises(ValueError, match="Name cannot be empty"):
        SignalChain(name="", platform="nam")

E2E Test (No Mocking)

test('creates signal chain', async ({ page }) => {
  await page.goto('/builder');
  await page.fill('[name="chain-name"]', 'My Chain');
  await page.click('button:has-text("Save")');

  // Verify in real database
  const response = await page.request.get('/api/v1/signal-chains');
  const chains = await response.json();
  expect(chains.signal_chains.some(c => c.name === 'My Chain')).toBe(true);
});

Parametrized Test

@pytest.mark.parametrize("platform,valid", [
    ("nam", True),
    ("aida_x", True),
    ("invalid", False),
])
def test_platform_validation(platform: str, valid: bool):
    if valid:
        chain = SignalChain(name="Test", platform=platform)
        assert chain.platform == platform
    else:
        with pytest.raises(ValueError):
            SignalChain(name="Test", platform=platform)

Mocking External APIs

async def test_t3k_sync_handles_error():
    """Mock EXTERNAL API only."""
    with patch("app.services.t3k_client.fetch_tones") as mock:
        mock.side_effect = ExternalAPIError("T3K down")
        result = await sync_service.sync_user_tones(user_id)
        assert result.status == "failed"

Test Data

Seed Data (from seed.sql)

User Username UUID Purpose
Primary e2e-user e2e00000-...-000000000001 Main test user
Other e2e-other-user e2e00000-...-000000000002 Permission tests

Test Data Prefix

Use E2E-Test- prefix for cleanup:

const chainName = `E2E-Test-My-Chain`;

Cleanup

  • Backend: Transaction rollback (automatic)
  • E2E: Delete E2E-Test-* entities in afterEach

Coverage

Target: 80%+

docker compose exec backend pytest tests/ --cov=app --cov-fail-under=80