test-generator
Skill4 starsChanged 10 months agoApache-2.0View on GitHub
Generate comprehensive test suites including static analysis, unit tests, integration tests, E2E tests, and coverage reports. Triggers: TG, test, 測試, 寫測試, coverage, 覆蓋率, pytest, unittest, 驗證.
Files
測試生成技能
描述
為指定的程式碼自動生成完整測試套件,包含靜態分析、單元測試、整合測試及覆蓋率報告。
觸發條件
- 「生成測試」、「寫測試」、「test this」
- 「建立單元測試」、「建立整合測試」
- 「靜態分析」、「type check」
- 「覆蓋率」、「coverage」
測試金字塔
/\
/ \ E2E Tests (少量)
/----\
/ \ Integration Tests (中等)
/--------\
/ \ Unit Tests (大量)
/------------\
/ Static Analysis (基礎)Python 測試策略
1️⃣ 靜態分析 (Static Analysis)
工具配置
| 工具 | 用途 | 配置檔 |
| mypy | 類型檢查 | pyproject.toml / mypy.ini |
| ruff | Linting + Formatting (取代 pylint/flake8/black) | pyproject.toml |
| bandit | 安全性掃描 | .bandit |
mypy 配置範例
# pyproject.toml
[tool.mypy]
python_version = "3.11"
strict = true
warn_return_any = true
warn_unused_ignores = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
no_implicit_optional = true
[[tool.mypy.overrides]]
module = ["tests.*"]
disallow_untyped_defs = falseruff 配置範例
# pyproject.toml
[tool.ruff]
target-version = "py311"
line-length = 88
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"I", # isort
"B", # flake8-bugbear
"C4", # flake8-comprehensions
"UP", # pyupgrade
"ARG", # flake8-unused-arguments
"SIM", # flake8-simplify
]
ignore = ["E501"] # line too long (handled by formatter)
[tool.ruff.isort]
known-first-party = ["src"]2️⃣ 單元測試 (Unit Tests)
pytest 配置
# pyproject.toml
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_functions = ["test_*"]
addopts = [
"-v",
"--strict-markers",
"--tb=short",
"-ra",
]
markers = [
"unit: Unit tests",
"integration: Integration tests",
"slow: Slow running tests",
]
filterwarnings = [
"error",
"ignore::DeprecationWarning",
]測試結構
tests/
├── __init__.py
├── conftest.py # 共用 fixtures
├── unit/ # 單元測試
│ ├── __init__.py
│ ├── conftest.py
│ ├── test_domain/ # Domain 層測試
│ ├── test_application/ # Application 層測試
│ └── test_utils/
├── integration/ # 整合測試
│ ├── __init__.py
│ ├── conftest.py
│ ├── test_api/
│ ├── test_database/
│ └── test_external/
└── e2e/ # 端對端測試
└── ...單元測試範例
# tests/unit/test_domain/test_user.py
import pytest
from src.domain.entities import User
from src.domain.exceptions import ValidationError
class TestUser:
"""User entity 單元測試"""
# === Happy Path ===
def test_create_user_with_valid_data(self):
"""正常建立使用者"""
user = User(name="Alice", email="alice@example.com")
assert user.name == "Alice"
assert user.email == "alice@example.com"
# === 邊界條件 ===
def test_create_user_with_minimum_name_length(self):
"""名稱最小長度"""
user = User(name="A", email="a@b.c")
assert len(user.name) == 1
@pytest.mark.parametrize("name,expected", [
("A" * 100, 100),
("中文名字", 4),
])
def test_name_length_variations(self, name: str, expected: int):
"""名稱長度變化測試"""
user = User(name=name, email="test@test.com")
assert len(user.name) == expected
# === 錯誤處理 ===
def test_create_user_with_empty_name_raises_error(self):
"""空名稱應拋出 ValidationError"""
with pytest.raises(ValidationError, match="Name cannot be empty"):
User(name="", email="test@test.com")
def test_create_user_with_invalid_email_raises_error(self):
"""無效 email 應拋出 ValidationError"""
with pytest.raises(ValidationError, match="Invalid email format"):
User(name="Test", email="not-an-email")
# === Null/None 處理 ===
def test_create_user_with_none_name_raises_error(self):
"""None 名稱應拋出 TypeError"""
with pytest.raises(TypeError):
User(name=None, email="test@test.com")3️⃣ 整合測試 (Integration Tests)
API 整合測試
# tests/integration/test_api/test_user_api.py
import pytest
from httpx import AsyncClient
from src.main import app
@pytest.mark.integration
@pytest.mark.asyncio
class TestUserAPI:
"""User API 整合測試"""
async def test_create_user_endpoint(self, async_client: AsyncClient):
"""POST /users 建立使用者"""
response = await async_client.post(
"/api/v1/users",
json={"name": "Test User", "email": "test@example.com"}
)
assert response.status_code == 201
data = response.json()
assert data["name"] == "Test User"
assert "id" in data
async def test_get_user_endpoint(self, async_client: AsyncClient, created_user):
"""GET /users/{id} 取得使用者"""
response = await async_client.get(f"/api/v1/users/{created_user.id}")
assert response.status_code == 200
assert response.json()["id"] == str(created_user.id)
async def test_get_nonexistent_user_returns_404(self, async_client: AsyncClient):
"""取得不存在的使用者應返回 404"""
response = await async_client.get("/api/v1/users/nonexistent-id")
assert response.status_code == 404資料庫整合測試
# tests/integration/test_database/test_user_repository.py
import pytest
from src.infrastructure.repositories import UserRepository
from src.domain.entities import User
@pytest.mark.integration
class TestUserRepository:
"""UserRepository 整合測試 (實際資料庫)"""
@pytest.fixture
def repository(self, db_session):
return UserRepository(session=db_session)
async def test_save_and_retrieve_user(self, repository: UserRepository):
"""儲存並取回使用者"""
user = User(name="Test", email="test@test.com")
saved_user = await repository.save(user)
retrieved = await repository.get_by_id(saved_user.id)
assert retrieved is not None
assert retrieved.name == "Test"
async def test_find_by_email(self, repository: UserRepository):
"""透過 email 查詢"""
user = User(name="Test", email="unique@test.com")
await repository.save(user)
found = await repository.find_by_email("unique@test.com")
assert found is not None
assert found.email == "unique@test.com"conftest.py (整合測試 fixtures)
# tests/integration/conftest.py
import pytest
import pytest_asyncio
from httpx import AsyncClient, ASGITransport
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker
from src.main import app
from src.infrastructure.database import Base
# === 測試資料庫 ===
TEST_DATABASE_URL = "sqlite+aiosqlite:///:memory:"
@pytest.fixture(scope="session")
def event_loop():
"""建立 event loop for async tests"""
import asyncio
loop = asyncio.get_event_loop_policy().new_event_loop()
yield loop
loop.close()
@pytest_asyncio.fixture(scope="function")
async def db_engine():
"""建立測試資料庫引擎"""
engine = create_async_engine(TEST_DATABASE_URL, echo=False)
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
yield engine
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.drop_all)
await engine.dispose()
@pytest_asyncio.fixture(scope="function")
async def db_session(db_engine):
"""建立測試資料庫 session"""
async_session = sessionmaker(
db_engine, class_=AsyncSession, expire_on_commit=False
)
async with async_session() as session:
yield session
await session.rollback()
# === HTTP Client ===
@pytest_asyncio.fixture
async def async_client():
"""建立非同步 HTTP 測試客戶端"""
transport = ASGITransport(app=app)
async with AsyncClient(transport=transport, base_url="http://test") as client:
yield client5️⃣ E2E 測試 (End-to-End Tests)
E2E 測試工具選擇
| 工具 | 適用場景 | 特點 |
| Playwright | Web UI 測試 | 跨瀏覽器、自動等待、截圖/錄影 |
| Selenium | 傳統 Web 測試 | 廣泛支援、成熟穩定 |
| pytest + httpx | API E2E | 輕量、快速 |
| Locust | 負載/效能測試 | 分散式、Python 原生 |
Playwright 配置 (推薦)
# pyproject.toml
[tool.pytest.ini_options]
markers = [
"e2e: End-to-end tests (require running application)",
]# tests/e2e/conftest.py
import pytest
from playwright.async_api import async_playwright, Browser, Page
@pytest.fixture(scope="session")
def browser_type():
"""可透過環境變數切換瀏覽器"""
import os
return os.getenv("BROWSER", "chromium") # chromium, firefox, webkit
@pytest.fixture(scope="session")
async def browser(browser_type: str):
"""建立瀏覽器實例 (session 級別)"""
async with async_playwright() as p:
browser = await getattr(p, browser_type).launch(
headless=True,
slow_mo=100, # 放慢操作以便觀察
)
yield browser
await browser.close()
@pytest.fixture
async def page(browser: Browser):
"""建立新頁面 (每個測試獨立)"""
context = await browser.new_context(
viewport={"width": 1280, "height": 720},
record_video_dir="test-results/videos", # 錄製影片
)
page = await context.new_page()
yield page
await context.close()
@pytest.fixture
def base_url():
"""應用程式 base URL"""
import os
return os.getenv("APP_URL", "http://localhost:8000")E2E 測試範例
# tests/e2e/test_user_journey.py
import pytest
from playwright.async_api import Page, expect
@pytest.mark.e2e
@pytest.mark.asyncio
class TestUserJourney:
"""使用者旅程 E2E 測試"""
async def test_user_registration_flow(self, page: Page, base_url: str):
"""測試完整註冊流程"""
# 1. 前往註冊頁面
await page.goto(f"{base_url}/register")
await expect(page).to_have_title("Register")
# 2. 填寫表單
await page.fill("input[name='username']", "testuser")
await page.fill("input[name='email']", "test@example.com")
await page.fill("input[name='password']", "SecureP@ss123")
await page.fill("input[name='confirm_password']", "SecureP@ss123")
# 3. 提交表單
await page.click("button[type='submit']")
# 4. 驗證結果
await expect(page).to_have_url(f"{base_url}/dashboard")
await expect(page.locator(".welcome-message")).to_contain_text("Welcome, testuser")
async def test_login_logout_flow(self, page: Page, base_url: str):
"""測試登入登出流程"""
# 登入
await page.goto(f"{base_url}/login")
await page.fill("input[name='email']", "test@example.com")
await page.fill("input[name='password']", "SecureP@ss123")
await page.click("button[type='submit']")
await expect(page.locator(".user-menu")).to_be_visible()
# 登出
await page.click(".logout-button")
await expect(page).to_have_url(f"{base_url}/")
async def test_create_item_flow(self, page: Page, base_url: str, authenticated_page):
"""測試建立項目流程 (需登入)"""
await authenticated_page.goto(f"{base_url}/items/new")
await authenticated_page.fill("input[name='title']", "Test Item")
await authenticated_page.fill("textarea[name='description']", "Description")
await authenticated_page.click("button[type='submit']")
await expect(authenticated_page.locator(".success-toast")).to_be_visible()API E2E 測試 (無 UI)
# tests/e2e/test_api_e2e.py
import pytest
import httpx
@pytest.mark.e2e
@pytest.mark.asyncio
class TestAPIEndToEnd:
"""API E2E 測試 - 測試完整 API 流程"""
@pytest.fixture
async def client(self, base_url: str):
async with httpx.AsyncClient(base_url=base_url) as client:
yield client
async def test_complete_crud_flow(self, client: httpx.AsyncClient):
"""測試完整 CRUD 流程"""
# Create
response = await client.post("/api/v1/items", json={"name": "Test"})
assert response.status_code == 201
item_id = response.json()["id"]
# Read
response = await client.get(f"/api/v1/items/{item_id}")
assert response.status_code == 200
assert response.json()["name"] == "Test"
# Update
response = await client.put(
f"/api/v1/items/{item_id}",
json={"name": "Updated"}
)
assert response.status_code == 200
# Delete
response = await client.delete(f"/api/v1/items/{item_id}")
assert response.status_code == 204
# Verify deletion
response = await client.get(f"/api/v1/items/{item_id}")
assert response.status_code == 4046️⃣ 覆蓋率 (Coverage)
pytest-cov 配置
# pyproject.toml
[tool.coverage.run]
source = ["src"]
branch = true
parallel = true
omit = [
"*/tests/*",
"*/__init__.py",
"*/migrations/*",
]
[tool.coverage.paths]
source = ["src"]
[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"def __repr__",
"raise AssertionError",
"raise NotImplementedError",
"if TYPE_CHECKING:",
"if __name__ == .__main__.:",
"@abstractmethod",
]
fail_under = 80
show_missing = true
skip_covered = true
[tool.coverage.html]
directory = "htmlcov"
[tool.coverage.xml]
output = "coverage.xml"執行覆蓋率
# 單元測試覆蓋率
pytest tests/unit -v --cov=src --cov-report=term-missing --cov-report=html
# 整合測試覆蓋率
pytest tests/integration -v --cov=src --cov-report=xml --cov-append
# 全部測試 + 覆蓋率報告
pytest --cov=src --cov-report=term-missing --cov-report=html --cov-report=xml測試框架對照表
| 語言 | 單元測試 | 整合測試 | 覆蓋率 | 靜態分析 |
| Python | pytest | pytest + httpx | pytest-cov | mypy, ruff, bandit |
| JavaScript | Jest / Vitest | Supertest | c8 / istanbul | ESLint, TypeScript |
| TypeScript | Jest / Vitest | Supertest | c8 / istanbul | tsc --noEmit, ESLint |
| Go | testing | testing + testcontainers | go test -cover | golangci-lint |
| Rust | cargo test | cargo test | cargo-tarpaulin | clippy |
CI 整合 Checklist
生成測試時應同步確認:
pyproject.toml包含完整測試配置requirements-dev.txt或pyproject.toml包含測試依賴- CI workflow 包含所有測試階段
- 覆蓋率門檻已設定(建議 ≥ 80%)
- 測試報告上傳至 CI artifacts
測試依賴 (Python)
# pyproject.toml [project.optional-dependencies] 或 requirements-dev.txt
[project.optional-dependencies]
dev = [
# Testing - Core
"pytest>=7.4.0",
"pytest-cov>=4.1.0",
"pytest-asyncio>=0.21.0",
"pytest-xdist>=3.3.0", # 平行測試
"pytest-mock>=3.11.0",
"pytest-timeout>=2.1.0",
"httpx>=0.24.0", # Async HTTP client for API tests
"factory-boy>=3.3.0", # Test data factories
"faker>=19.0.0", # Fake data generation
# E2E Testing
"playwright>=1.40.0", # Browser automation
"pytest-playwright>=0.4.0", # Playwright pytest plugin
"locust>=2.20.0", # Load testing (optional)
# Static Analysis
"mypy>=1.5.0",
"ruff>=0.0.290",
"bandit[toml]>=1.7.5",
# Type stubs
"types-requests",
"types-python-dateutil",
]輸出格式
## 測試套件生成報告
### 📁 檔案結構
[生成的測試目錄結構]
### 📋 測試清單
#### 靜態分析
- [ ] mypy 類型檢查
- [ ] ruff linting
- [ ] bandit 安全掃描
#### 單元測試 (`tests/unit/`)
- ✅ 正常流程 (Happy Path)
- ✅ 邊界條件 (Edge Cases)
- ✅ 錯誤處理 (Error Handling)
- ✅ Null/None 處理
#### 整合測試 (`tests/integration/`)
- ✅ API 端點測試
- ✅ 資料庫操作測試
- ✅ 外部服務測試 (mocked)
#### E2E 測試 (`tests/e2e/`)
- ✅ 使用者旅程測試
- ✅ 關鍵流程驗證
- ✅ 跨瀏覽器測試 (Playwright)
### 📊 覆蓋率目標
- 單元測試:≥ 90%
- 整合測試:≥ 70%
- 總體覆蓋:≥ 80%
### ⚙️ 執行指令
[相關測試執行命令]Other versions24 projects use a version of this.
Word for word · 0
None found.
Edited · 2
- u9401066/template-is-all-you-need★ 387% same
- u9401066/asset-aware-mcp★ 0100% same
Wrote their own version · 22
- laolaoshiren/claude-code-skills-zh★ 899same name
- u9401066/pubmed-search-mcp★ 28same name
- Ecoa-PUC-Rio/GAIDE★ 7same name
- jcarlos78/GAIDE★ 7same name
- bestdeejay-design/agent-skills★ 6same name
- Data-Wise/craft★ 4same name
- Nembie/claude-code-skills★ 3same name
- suxiao1824308603/Qoder-devtools-skills★ 3same name
- Dokkabei97/forged-claude-code★ 2same name
- jablonkai/skills★ 2same name
12 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
Used alongsideProjects with it also use refactor-advisor, log-analyzer and 3 more.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
aidankaokaoSkillrefactor-advisor
分析程式碼的重構機會,給出有優先順序的建議,但不直接改寫程式碼。當使用者說「這段程式碼怎麼改比較好」、「幫我分析重構方向」、「這樣寫有什麼問題」、「refactor」、「技術債」時觸發。
Also in 3 projects that use this
nek1987Skilllog-analyzer
Parse and analyze application logs to identify errors, patterns, and insights.
Also in 3 projects that use this
JeffallanSkillsecurity-reviewer
Identifies security vulnerabilities, generates structured audit reports with severity ratings, and provides actionable remediation guidance. Use when conducting security audits, reviewing code for vulnerabilities, or analyzing infrastructure security. Invoke for SAST scans, penetration testing, DevSecOps practices, cloud security reviews, dependency audits, secrets scanning, or compliance checks. Produces vulnerability reports, prioritized recommendations, and compliance checklists.
Also in 3 projects that use this
Microsoft AzureSkillgit-workflow
Squad branching model: dev-first workflow with insiders preview channel
Also in 3 projects that use this
GoogleSkillcode-review
Adversarial systems, concurrency, memory-model, and architecture code review protocol. Use this skill whenever asked to review code, PRs, diffs, or branches in Filament.
Also in 4 projects that use this
In this repository22 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- .github/copilot-instructions.mdCopilot instructions
- changelog-updaterSkill
- code-refactorSkill
- code-reviewerSkill
- ddd-architectSkill
- git-doc-updaterSkill
- git-precommitSkill
- memory-checkpointSkill
- memory-updaterSkill
- nsforge-code-generationSkill
- nsforge-derivation-workflowSkill
- nsforge-formula-managementSkill
- nsforge-formula-searchSkill
- nsforge-quick-calculateSkill
- nsforge-usolver-collabSkill
- nsforge-verification-suiteSkill
- project-initSkill
- readme-i18nSkill
- readme-updaterSkill
- roadmap-updaterSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

