搜索

PythonReactSEOPDF
GitHub Skill

testing-core

Cross-language testing patterns: unit, integration, E2E testing strategies. Use when: writing tests, setting up test infrastructure, or improving coverage. Triggers: "test", "testing", "unit test", "integration test", "e2e", "coverage", "mock", "fixture".

czer323☆ 0 仓库 Stars0 仓库 Forks

安装与使用

请根据来源仓库和你的 AI 工具说明安装此技能。先阅读技能文件和依赖要求,再添加到工作流。

查看来源 ↗使用指南

技能说明

Testing Core

Quick Reference

Topic Reference
Mocking mocking.md — Python, TypeScript, Rust mocking patterns

Test Pyramid

        /  E2E  \        Few, slow, expensive
       /─────────\
      / Integration \    Some, medium speed
     /───────────────\
    /      Unit        \  Many, fast, cheap
   /─────────────────────\

When to Use Each

Type Tests What Speed When
Unit Single function/class Fast Pure logic, calculations
Integration Components together Medium DB queries, API calls
E2E Full user flow Slow Critical paths only

Test Structure (AAA)

def test_user_creation():
    # Arrange - Setup test data
    user_data = {"email": "test@example.com", "name": "Test"}

    # Act - Execute the code
    user = user_service.create(user_data)

    # Assert - Verify results
    assert user.email == "test@example.com"
    assert user.id is not None

Naming Convention

test_<what>_<scenario>_<expected>

# Examples:
test_create_user_valid_data_returns_user()
test_create_user_duplicate_email_raises_error()
test_get_user_not_found_returns_none()

Unit Test Patterns

Testing Pure Functions
# Function
def calculate_discount(price: float, percentage: float) -> float:
    return price * (1 - percentage / 100)

# Test
def test_calculate_discount():
    assert calculate_discount(100, 10) == 90
    assert calculate_discount(50, 50) == 25
    assert calculate_discount(100, 0) == 100
Testing Edge Cases
@pytest.mark.parametrize("input,expected", [
    ("", False),           # Empty
    ("a@b.c", True),       # Minimal valid
    ("test@example.com", True),
    ("invalid", False),    # No @
    ("@example.com", False), # No local part
    ("test@", False),      # No domain
])
def test_validate_email(input, expected):
    assert validate_email(input) == expected
Testing Exceptions
def test_divide_by_zero_raises():
    with pytest.raises(ZeroDivisionError):
        divide(10, 0)

def test_invalid_input_raises_with_message():
    with pytest.raises(ValueError) as exc_info:
        process_data(None)
    assert "cannot be None" in str(exc_info.value)

Integration Test Patterns

Database Tests
@pytest.fixture
async def db_session():
    # Setup
    async with engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)

    async with AsyncSession(engine) as session:
        yield session

    # Teardown
    async with engine.begin() as conn:
        await conn.run_sync(Base.metadata.drop_all)

async def test_create_and_fetch_user(db_session):
    # Create
    user = User(email="test@example.com", name="Test")
    db_session.add(user)
    await db_session.commit()

    # Fetch
    result = await db_session.get(User, user.id)
    assert result.email == "test@example.com"
API Tests
async def test_api_create_user(client):
    response = await client.post(
        "/users",
        json={"email": "test@example.com", "name": "Test"}
    )

    assert response.status_code == 201
    data = response.json()
    assert data["email"] == "test@example.com"
    assert "id" in data

Mocking Patterns

When to Mock
Mock:
├─ External APIs (payment, email, SMS)
├─ Time/dates (for deterministic tests)
├─ Random values
└─ Slow operations

Don't Mock:
├─ The code you're testing
├─ Simple data structures
└─ Things you can use real instances of
Mock Examples
# Python
from unittest.mock import Mock, patch, AsyncMock

@patch("services.email.send_email")
def test_signup_sends_email(mock_send):
    mock_send.return_value = True
    signup("test@example.com")
    mock_send.assert_called_once_with("test@example.com", subject=ANY)

# Async mock
@patch("services.payment.charge", new_callable=AsyncMock)
async def test_checkout(mock_charge):
    mock_charge.return_value = {"id": "ch_123"}
    result = await checkout(cart)
    assert result.payment_id == "ch_123"
// TypeScript (Vitest)
import { vi } from 'vitest';

vi.mock('./email', () => ({
  sendEmail: vi.fn().mockResolvedValue(true)
}));

test('signup sends email', async () => {
  await signup('test@example.com');
  expect(sendEmail).toHaveBeenCalledWith('test@example.com', expect.any(String));
});

Fixtures & Factories

Fixture Pattern
@pytest.fixture
def user():
    return User(id=1, email="test@example.com", name="Test")

@pytest.fixture
def admin_user(user):
    user.role = "admin"
    return user

def test_with_fixtures(user, admin_user):
    assert user.role != "admin"
    assert admin_user.role == "admin"
Factory Pattern
def create_user(**overrides):
    defaults = {
        "email": f"test-{uuid4()}@example.com",
        "name": "Test User",
        "role": "user"
    }
    return User(**{**defaults, **overrides})

def test_with_factory():
    user = create_user(role="admin")
    assert user.role == "admin"

Coverage Goals

Type Target Notes
Unit tests 80%+ Focus on logic
Integration Key paths DB operations, APIs
E2E Critical flows Signup, checkout
What NOT to Test
  • Framework code (it's already tested)
  • Trivial getters/setters
  • Third-party libraries
  • Generated code

Smoke Tests (CRITICAL)

Smoke tests verify the app actually starts and responds. Unit tests can pass while the app is completely broken.

Why Smoke Tests Matter
Scenario: All unit tests pass, app doesn't start

Unit tests:
✓ test_create_user_returns_user
✓ test_delete_user_removes_from_db
✓ test_list_users_returns_all

Reality:
$ cargo run
Error: main.rs missing or broken
Smoke Test Pattern (Rust/Axum)
// REQUIRED: Every project needs this test
#[tokio::test]
async fn test_health_check_responds() {
    let server = TestServer::new(create_app()).unwrap();
    let response = server.get("/health").await;
    response.assert_status_ok();
}
Smoke Tests for Fullstack (HTMX)
#[tokio::test]
async fn test_index_page_is_usable() {
    let server = TestServer::new(create_app()).unwrap();
    let response = server.get("/").await;
    let html = response.text();

    // Page loads
    assert!(html.contains("<!DOCTYPE html>"));

    // Has create form
    assert!(html.contains("hx-post"), "Must have HTMX form");

    // Has submit button
    assert!(html.contains(r#"type="submit""#));
}
Verification Gate Smoke Test

The verify.py script automatically:

  1. Builds the app (cargo build)
  2. Starts it (cargo run)
  3. Waits for /health to respond
  4. Fails if app doesn't start within 10 seconds

Anti-patterns

Don't Do Instead
Test implementation details Test behavior
Duplicate prod logic in tests Use different approach
Flaky tests Fix or delete
Test everything Test what matters
Mock everything Use real when possible
Giant test functions Small, focused tests
Skip smoke tests Always test app starts
Skip UI presence tests Verify forms/buttons exist

使用示例

查看来源仓库中的使用示例与配置说明。

查看来源 ↗

来源历史

收录时间
2026-01-20
索引更新时间
2026-03-03
Branch
main

收录与同步日期属于本站记录。源码修改历史请查看来源仓库。

GitHub ↗

chat Comments (0)

chat_bubble_outline

No comments yet. Be the first to share your thoughts!