AGENT SKILL

testing-strategy

Testing strategy — unit/integration/e2e pyramid, TDD workflow, mocking best practices, test isolation, coverage targets

License 许可
Apache-2.0
最近更新
2026-11-14
Stars
★ 13
Forks
2

01 前言 Front Matter

name
testing-strategy
see_also
[ "ai-code-maintainability", "error-handling-patterns", "type-safety-patterns" ]
triggers
[ "testing strategy", "test pyramid", "tdd workflow", "unit test best practice", "mock best practice", "test isolation", "integration test", "test coverage target", "pytest best practice", "vitest jest best practice" ]
description
Testing strategy — unit/integration/e2e pyramid, TDD workflow, mocking best practices, test isolation, coverage targets
do_not_use_for
[ "LLM evaluation — use ragas/deepeval", "e2e browser testing — use playwright patterns", "writing tests for specific code — use tdd-guide" ]

03 SKILL.md 正文

Testing Strategy

Test Pyramid

         [E2E]           ← few, slow, expensive, test real flows
       [Integration]     ← some, medium speed, real DB/API
     [Unit]              ← many, fast, isolated, mock externals

Target ratios: 70% unit, 20% integration, 10% e2e Coverage targets: new code ≥ 80%, overall project ≥ 60%

Python: pytest Best Practices

# conftest.py — shared fixtures
import pytest
from sqlalchemy import create_engine
from sqlalchemy.orm import Session

@pytest.fixture(scope="session")
def engine():
    return create_engine("sqlite:///:memory:")   # test DB

@pytest.fixture
def db(engine) -> Session:
    Base.metadata.create_all(engine)
    with Session(engine) as session:
        yield session
        session.rollback()   # clean up after each test

@pytest.fixture
def user(db: Session) -> User:
    u = User(name="Test User", email="test@example.com")
    db.add(u)
    db.flush()
    return u
# test_user_service.py — unit test with mock
import pytest
from unittest.mock import AsyncMock, patch, MagicMock
from my_app.user_service import UserService, EmailSender

class TestUserService:
    def test_create_user_sends_welcome_email(self):
        # Arrange
        mock_sender = MagicMock(spec=EmailSender)
        mock_repo = MagicMock()
        mock_repo.save.return_value = User(id="u-1", name="Alice", email="a@b.com")
        service = UserService(repo=mock_repo, sender=mock_sender)

        # Act
        result = service.create_user("Alice", "a@b.com")

        # Assert
        assert result.name == "Alice"
        mock_sender.send.assert_called_once_with(
            to="a@b.com",
            subject="Welcome!",
        )

    @pytest.mark.asyncio
    async def test_async_method(self):
        mock_api = AsyncMock()
        mock_api.fetch.return_value = {"status": "ok"}
        service = MyService(api=mock_api)
        result = await service.do_thing()
        assert result == "ok"

TypeScript: Vitest Best Practices

// user.service.test.ts
import { describe, it, expect, vi, beforeEach } from "vitest";
import { UserService } from "./user.service";

// Mock at module level
vi.mock("./email.service", () => ({
  EmailService: vi.fn().mockImplementation(() => ({
    send: vi.fn().mockResolvedValue(undefined),
  })),
}));

describe("UserService", () => {
  let service: UserService;
  let mockEmailService: { send: ReturnType<typeof vi.fn> };

  beforeEach(() => {
    mockEmailService = { send: vi.fn().mockResolvedValue(undefined) };
    service = new UserService({ emailService: mockEmailService });
  });

  it("sends welcome email on user creation", async () => {
    const user = await service.createUser({ name: "Alice", email: "a@b.com" });

    expect(user.name).toBe("Alice");
    expect(mockEmailService.send).toHaveBeenCalledOnce();
    expect(mockEmailService.send).toHaveBeenCalledWith(
      expect.objectContaining({ to: "a@b.com" })
    );
  });

  it("throws on duplicate email", async () => {
    await service.createUser({ name: "Alice", email: "dup@b.com" });
    await expect(
      service.createUser({ name: "Bob", email: "dup@b.com" })
    ).rejects.toThrow("email already exists");
  });
});

Integration Test Pattern

# test_order_api.py — integration: real DB, real HTTP
import pytest
import httpx
from fastapi.testclient import TestClient

@pytest.fixture(scope="module")
def client() -> TestClient:
    from my_app.main import app
    return TestClient(app)

def test_create_order_full_flow(client: TestClient, db: Session):
    # Create user
    user_resp = client.post("/users", json={"name": "Alice", "email": "a@b.com"})
    assert user_resp.status_code == 201
    user_id = user_resp.json()["id"]

    # Create order
    order_resp = client.post("/orders", json={"user_id": user_id, "items": [{"sku": "A", "qty": 2}]})
    assert order_resp.status_code == 201
    order = order_resp.json()
    assert order["status"] == "pending"
    assert order["user_id"] == user_id

TDD Workflow

# RED: write failing test first
pytest tests/test_feature.py::test_new_behavior -v
# → FAILED (expected)

# GREEN: minimal implementation to pass
# Write only enough code to make the test pass

pytest tests/test_feature.py::test_new_behavior -v
# → PASSED

# REFACTOR: clean up without breaking tests
pytest tests/test_feature.py -v
# → All PASSED

What NOT to Mock

# ✅ Mock external I/O (network, DB, file system, time)
# ✅ Mock slow operations (sleep, large computations)
# ✅ Mock non-deterministic values (random, uuid, datetime.now)

# ❌ Don't mock your own domain logic
# ❌ Don't mock data transformations — test them directly
# ❌ Don't mock to avoid test complexity — fix the design instead

# ❌ Over-mocking — this test proves nothing
def test_user_service_create():
    service = MagicMock(spec=UserService)
    service.create.return_value = User(id="1")
    result = service.create(...)
    assert result.id == "1"  # testing the mock

Anti-Fake-Pass Checks

  • Test that fails before implementation = TDD red phase — skip this = untested code
  • assert_called_once_with() is strict — assert_called_with() only checks the last call
  • scope="session" fixtures are shared across tests — don't mutate them in individual tests
  • asyncio tests need @pytest.mark.asyncio or asyncio_mode = "auto" in pytest.ini
  • Integration tests hitting real DB need cleanup — use session.rollback() or transaction per test
  • Coverage ≥ 80% on new code — measure with pytest --cov=src --cov-fail-under=80

04 同仓库更多 Skills

Horonomos-laps
Horonomos-laps/Yana-AI

openai--sentry--sentry

Use when the user asks to inspect Sentry issues or events, summarize recent production errors, or pull basic Sentry health data via the Sentry API; perform read-only queries with the bundled script and require `SENTRY_AUTH_TOKEN`.

★ 13
Horonomos-laps
Horonomos-laps/Yana-AI

ito-basket-compare

Compare Itô prediction-market baskets against a user's knowledge base, portfolio notes, financial context, watchlist, or research thesis. Use for read-only basket comparison and gap analysis without investment advice or live trading.

★ 13
Horonomos-laps
Horonomos-laps/Yana-AI

implementing-network-traffic-baselining

Build network traffic baselines from NetFlow/IPFIX data using Python pandas for statistical analysis, z-score anomaly detection, and hourly/daily traffic pattern profiling

★ 13
Horonomos-laps
Horonomos-laps/Yana-AI

detecting-evasion-techniques-in-endpoint-logs

Detects defense evasion techniques used by adversaries in endpoint logs including log tampering, timestomping, process injection, and security tool disabling. Use when investigating suspicious endpoint behavior, building detection rules for evasion tactics, or conducting threat hunting for stealthy adversary activity. Activates for requests involving evasion detection, defense evasion analysis, log tampering detection, or MITRE ATT&CK TA0005.

★ 13
Horonomos-laps
Horonomos-laps/Yana-AI

implementing-microsegmentation-with-guardicore

Implementing microsegmentation using Akamai Guardicore Segmentation to map application dependencies, create granular network policies, visualize east-west traffic flows, and enforce least-privilege communication between workloads across data centers and cloud.

★ 13
Horonomos-laps
Horonomos-laps/Yana-AI

ramdisk-tmpfs-patterns

暂无简介

★ 13