Token导航 LogoToken导航

Agent Skill · 效率

clean-pytest(干净的 pytest)

用于辅助 Python 项目开发、测试、依赖管理和常见框架工作流。它适合让 Agent 阅读 Python 代码、定位测试问题、整理运行命令、生成脚本或分析数据处理逻辑。使用时需要确认项目虚拟环境、依赖版本和测试入口;涉及执行脚本、读写文件、访问数据库或调用外部 API 时,应先明确运行目录和输入输出范围,避免误改生产数据。

clean-pytest 是效率类 Agent Skill,可用宿主为 OpenClaw,通过ClawHub获取,权限为调用外部服务;许可证为 MIT-0,第三方扫描已通过,按安装量在效率类的 21,003 个 Skill 里排第 2,258。

总安装 27,132周安装 1,087GitHub Stars 公开资料未说明下载 8,783核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
MIT-0
版本
0.1.0

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:clean-pytest(干净的 pytest)
来源仓库:https://github.com/marcoracer/clean-pytest
安装命令:
openclaw skills install clean-pytest
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 OpenClaw 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

ClawHubOpenClaw
openclaw skills install clean-pytest

这个 Skill 是做什么的

效率类 Skill 把重复的整理、汇总、转换工作交给助手完成,减少手工复制粘贴。

clean-pytest 指导使用 Fake 测试与契约测试编写可维护的 Python 测试用例。适合在 OpenClaw 中为 pytest 项目建立清晰测试套件时使用。通过 clawhub 安装,命令为 openclaw skills install clean-pytest。

使用前需确认项目依赖与环境配置,注意避免测试污染生产数据。建议结合来源仓库和 README 文档进一步核验具体功能和使用细节。

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 OpenClaw;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 openclaw skills install clean-pytest;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要调用外部服务,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

clean-pytest 怎么安装?

在终端执行 openclaw skills install clean-pytest。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

clean-pytest 支持哪些 AI 工具?

公开资料标注支持 OpenClaw。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

clean-pytest 需要什么权限?

权限级别是调用外部服务。该 Skill 可能调用第三方服务、云服务或外部模型 API,使用前需要确认账号、额度、数据发送范围和服务条款。

clean-pytest 是免费的吗?

来源标注的许可证是 MIT-0。本站不代收任何费用,也不托管安装包;能否商用、要不要署名,以许可证原文为准。

clean-pytest 用的人多吗?

公开数据是累计安装 27,132 次,近一周安装 1,087 次,下载 8,783 次。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

clean-pytest 安全吗?

第三方扫描结果为通过,但这只覆盖已知的静态检查项,不能替代自己读一遍源码和安装命令。

值得选的地方和要先想清楚的地方

值得选的地方

  • 专注 OpenClaw 一种宿主,触发方式和使用习惯不用来回切换。
  • 许可证明确为 MIT-0,商用和分发边界好判断。
  • 第三方扫描已通过,常见的高风险行为没有命中。

先想清楚的地方

  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 OpenClaw,想直接扩展它能力的人。
  • 需要处理效率类任务、又不想每次都手写提示词的人。
  • 需要许可证清楚、并且有第三方扫描结果的团队项目。

不太适合

  • 需要厂商级技术支持和 SLA 的生产系统。

效率类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
github

GitHub 代码协作 · OpenClaw

7,632,918545需要联网MIT-0通过
nano-pdf

Nano PDF 文档 · OpenClaw

4,249,476231需要联网MIT-0通过
auto-updater

自动更新程序 · OpenClaw

2,964,063386操作浏览器MIT-0通过
video-frames

视频帧 · OpenClaw

2,621,304112需要联网MIT-0通过
gemini

Gemini 助手 · OpenClaw

2,547,39648需要联网MIT-0有提醒
clean-pytest

干净的 pytest · 本页

27,132—调用外部服务MIT-0通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

name
clean-pytest
description
Write clean, maintainable pytest tests using Fake-based testing, contract testing, and dependency injection patterns. Use when setting up test suites for Python/MCP projects, creating Fakes for external dependencies, writing contract tests, or implementing test patterns with fixtures and parametrization.
license
MIT
metadata
emoji
🧪
homepage
https://github.com/numinstante/skills
os
[darwin, linux, windows]
tags
[python, pytest, testing, tdd, mcp, contract-testing]
requires
anyBins
[python3]

Clean Pytest

Clean, maintainable pytest test patterns using Fake-based testing, contract testing, and dependency injection. Focuses on test isolation, reusability, and clarity through explicit AAA pattern and well-structured fixtures.

When to Use

  • Setting up test suites for Python/MCP projects
  • Creating Fake implementations for external dependencies
  • Writing contract tests for MCP tools/controllers
  • Implementing test patterns with dependency injection
  • Testing layered architectures (Controllers → Services → Repositories)
  • Writing parametrized tests for multiple scenarios

Core Principles

1. Fakes over Mocks

Use Fake classes instead of mocking with unittest.mock. Fakes are in-memory implementations that mimic real dependencies without external calls.

Why Fakes?

  • More readable and maintainable
  • Easier to debug
  • Better test isolation
  • No monkey-patching magic
  • Self-documenting behavior

2. Explicit AAA Pattern

Structure every test into three clear phases with comments:

# Arrange
# Set up test data and dependencies

# Act
# Execute the code under test

# Assert
# Verify the result

3. Dependency Injection in Fixtures

Inject dependencies between fixtures to maintain relationships and avoid duplication.

4. Contract Testing

Verify that components register tools/functions correctly and pass expected arguments.

Architecture Pattern

Controller (MCP Tools)
    ↓
Service (Business Logic)
    ↓
Repository (Data Access)
    ↓
Fake (Test Implementation)

Creating Fakes

Basic Fake Structure

Create a Fake class that implements the same interface as the real dependency:

# tests/fakes.py
from typing import Any, Dict, List, Optional

class FakeAuth:
    """Fake implementation of AuthProvider for testing."""
    def __init__(self) -> None:
        self.created: List[Dict[str, Any]] = []
        self.deleted: List[str] = []
        self._seq = 0
        self.fail_on_create: bool = False

    def create_user(self, email: str, password: str, display_name: str) -> str:
        if self.fail_on_create:
            raise RuntimeError("create_user failed (fake)")
        self._seq += 1
        uid = f"uid-{self._seq}"
        rec = {"uid": uid, "email": email, "display_name": display_name}
        self.created.append(rec)
        return uid

    def delete_user(self, uid: str) -> None:
        self.deleted.append(uid)

Repository Fake

class FakeUsersRepo:
    """Fake implementation of UsersRepository."""
    def __init__(self) -> None:
        self.users: Dict[str, Dict[str, Any]] = {}
        self.fail_on_upsert: bool = False

    def upsert_user_doc(self, uid: str, data: Dict[str, Any]) -> None:
        if self.fail_on_upsert:
            raise RuntimeError("upsert_user_doc failed (fake)")
        self.users[uid] = dict(data)

    def list_users(self, limit: Optional[int] = None) -> List[Dict[str, Any]]:
        items = list(self.users.values())
        if limit and limit > 0:
            items = items[:limit]
        return [dict(it) for it in items]

Controlled Failure Fakes

class FakeAuth:
    def __init__(self) -> None:
        self.fail_on_create: bool = False  # Control failure in tests

    def create_user(self, email: str, password: str, display_name: str) -> str:
        if self.fail_on_create:
            raise RuntimeError("create_user failed (fake)")
        # ... rest of implementation

Nested Repository Fakes

class FakeSectorsRepo:
    def __init__(self, institutions: FakeInstitutionsRepo | None = None) -> None:
        self.institutions = institutions  # Inject dependency
        self.data: Dict[str, Dict[str, Dict[str, Any]]] = {}

    def institution_exists(self, institution_id: str) -> bool:
        return bool(self.institutions and institution_id in self.institutions.data)

    def upsert_sector(self, institution_id: str, sector_id: str, data: Dict[str, Any]) -> None:
        self.data.setdefault(institution_id, {})[sector_id] = dict(data)

Fixtures

Basic Fixture (conftest.py)

# tests/conftest.py
import pytest
from tests.fakes import FakeAuth, FakeUsersRepo

@pytest.fixture()
def fake_auth():
    """Provide a fresh FakeAuth for each test."""
    return FakeAuth()

@pytest.fixture()
def fake_users_repo():
    """Provide a fresh FakeUsersRepo for each test."""
    return FakeUsersRepo()

Fixture with Dependency Injection

@pytest.fixture()
def fake_sectors_repo(fake_institutions_repo):
    """FakeSectorsRepo depends on FakeInstitutionsRepo."""
    return FakeSectorsRepo(institutions=fake_institutions_repo)

@pytest.fixture()
def fake_rooms_repo(fake_sectors_repo):
    """FakeRoomsRepo depends on FakeSectorsRepo."""
    return FakeRoomsRepo(sectors=fake_sectors_repo)

Environment Fixture

@pytest.fixture()
def user_env(fake_auth, fake_users_repo):
    """Provide service and all dependencies for user operations."""
    from myapp.services.user_service import UserService
    svc = UserService(fake_auth, fake_users_repo)
    return svc, fake_auth, fake_users_repo

Seeded Environment Fixture

@pytest.fixture()
def user_env_seeded(user_env):
    """Environment with pre-seeded data."""
    svc, auth, repo = user_env
    svc.add_user(email="test@example.com", password="secret", name="Test User")
    return svc

Fixture with Cleanup

@pytest.fixture()
def temp_file():
    """Provide a temporary file and clean up after test."""
    import tempfile
    import os
    fd, path = tempfile.mkstemp()
    os.close(fd)
    yield path
    os.unlink(path)

Service Layer Testing

Basic AAA Pattern Test

# tests/test_user_service.py
import pytest
from myapp.services.user_service import UserService

def test_add_user_success(fake_auth, fake_users_repo):
    # Arrange
    svc = UserService(fake_auth, fake_users_repo)
    email = "test@example.com"
    password = "secret"
    name = "Test User"

    # Act
    result = svc.add_user(email=email, password=password, name=name)

    # Assert
    assert result["status"] == "ok"
    assert result["user"]["email"] == email
    assert result["user"]["name"] == name
    assert result["uid"] in fake_users_repo.users

Parametrized Tests

@pytest.mark.parametrize(
    "email,password,name,role",
    [
        ("a@example.com", "secret", "Alice", "admin"),
        ("b@example.com", "p@ss", "Bob", "user"),
    ],
)
def test_add_user_parametrized(user_env, email, password, name, role):
    svc, _auth, _repo = user_env

    # Act
    res = svc.add_user(email=email, password=password, name=name, global_role=role)

    # Assert
    assert res["status"] == "ok"
    assert res["user"]["email"] == email
    assert res["user"]["name"] == name
    assert res["user"]["globalRole"] == role

Testing Error Scenarios with Fakes

@pytest.mark.parametrize("email", ["c@example.com", "d@example.com"])
def test_add_user_rollback_on_firestore_failure(fake_auth, fake_users_repo, email):
    # Arrange
    fake_users_repo.fail_on_upsert = True
    svc = UserService(fake_auth, fake_users_repo)

    # Act & Assert
    with pytest.raises(RuntimeError):
        svc.add_user(email=email, password="secret", name="Bob")

    # Assert rollback
    assert fake_auth.deleted, "Expected auth user to be deleted on Firestore failure"

Testing Timestamp Normalization

def test_list_users_normalizes_timestamps_to_iso(user_env):
    # Arrange
    svc, _auth, repo = user_env
    from datetime import datetime
    repo.users["u1"] = {
        "id": "u1",
        "email": "x@y.z",
        "name": "X",
        "globalRole": "user",
        "createdAt": datetime(2024, 1, 1),
        "updatedAt": datetime(2024, 1, 2),
    }

    # Act
    res = svc.list_users(limit=10)

    # Assert
    assert res["status"] == "ok"
    assert res["count"] == 1
    user = res["users"][0]
    assert isinstance(user["createdAt"], str)
    assert isinstance(user["updatedAt"], str)

Contract Testing

MCP Tool Registration Contract

Test that controllers properly register tools with expected signatures:

# tests/test_controllers_contract.py
from typing import Any, Callable, Dict

class FakeMCP:
    """Minimal FakeMCP for contract testing."""
    def __init__(self) -> None:
        self.tools: Dict[str, Callable[..., Any]] = {}
        self.meta: Dict[str, Dict[str, Any]] = {}

    def tool(self, name: str, description: str, tags: Optional[set] = None, meta: Optional[dict] = None):
        def decorator(fn: Callable[..., Any]):
            self.tools[name] = fn
            self.meta[name] = {
                "description": description,
                "tags": set(tags or set()),
                "meta": dict(meta or {}),
            }
            return fn
        return decorator


class FakeUserService:
    """Simple fake service that records calls."""
    def __init__(self):
        self.calls = []

    def add_user(self, **kwargs):
        self.calls.append(("add_user", kwargs))
        return {"status": "ok", "op": "add_user", "args": kwargs}


def test_users_controller_contract():
    # Arrange
    from myapp.controllers.users_controller import UsersController
    fake = FakeMCP()
    svc = FakeUserService()
    UsersController(fake, svc)

    # Assert tool registration
    assert "add_user" in fake.tools
    assert "list_users" in fake.tools

    # Act & Assert tool behavior
    res = fake.tools["add_user"](
        email="a@x.y", password="s3cr3t", name="Alice", global_role="admin"
    )
    assert res["status"] == "ok"
    assert res["op"] == "add_user"
    assert res["args"]["email"] == "a@x.y"

Parametrized Contract Tests

@pytest.mark.parametrize(
    "email,password,name,role",
    [
        ("a@x.y", "s3cr3t", "Alice", "admin"),
        ("b@x.y", "p@ssw0rd", "Bob", "user"),
    ],
)
def test_users_add_user_parametrized(_users_env, email, password, name, role):
    # Arrange
    fake, _ = _users_env

    # Act
    res = fake.tools["add_user"](
        email=email, password=password, name=name, global_role=role
    )

    # Assert
    assert res["status"] == "ok"
    assert res["op"] == "add_user"
    assert res["args"]["email"] == email

Repository Layer Testing

Testing Repository Operations

@pytest.fixture()
def repo_env(fake_institutions_repo, fake_sectors_repo):
    # Seed data
    fake_institutions_repo.upsert("inst1", {"id": "inst1", "name": "Inst One"})
    fake_sectors_repo.upsert_sector(
        "inst1", "er", {"id": "er", "name": "ER", "slug": "er", "isActive": True}
    )
    return fake_sectors_repo

Testing Multiple Data Scenarios

@pytest.mark.parametrize("rooms", [
    ["101"],
    ["201", {"name": "102", "id": "room-102"}],
])
def test_add_and_list_rooms(room_env, rooms):
    svc, _ = room_env

    # Act
    res = svc.add_sector_rooms("inst1", "er", rooms)

    # Assert
    assert res["status"] == "ok"
    assert res["count"] == len(rooms)

    lst = svc.list_sector_rooms("inst1", "er", limit=10)
    assert lst["status"] == "ok"
    assert lst["count"] == len(rooms)

Testing Limit Behavior

@pytest.mark.parametrize("limit", [1, 3])
def test_list_rooms_limits(room_env_seeded, limit):
    svc = room_env_seeded

    # Act
    lst = svc.list_sector_rooms("inst1", "er", limit=limit)

    # Assert
    assert lst["status"] == "ok"
    assert lst["count"] == min(2, limit)  # 2 items seeded

Testing Not Found Scenarios

@pytest.mark.parametrize("room_id,deleted", [
    ("room-102", True),
    ("room-999", False),
])
def test_remove_rooms_parametrized(room_env_seeded, room_id, deleted):
    svc = room_env_seeded

    # Act
    res = svc.remove_sector_room("inst1", "er", room_id)

    # Assert
    assert res["deleted"] is deleted
    if not deleted:
        assert res.get("reason") == "room_not_found"

Integration Testing

Conditional Integration Tests

Skip integration tests when external dependencies are not available:

# tests/test_integration_wiring.py
import os
import pytest

# Gate this integration test on presence of credentials
_ENV_KEYS = (
    "FIREBASE_SERVICE_ACCOUNT",
    "GOOGLE_APPLICATION_CREDENTIALS",
)
_has_env_creds = any(os.getenv(k) for k in _ENV_KEYS)

pytestmark = [
    pytest.mark.integration,
    pytest.mark.skipif(
        not _has_env_creds,
        reason=(
            "Integration test requires Firebase Admin credentials via env "
            "(FIREBASE_SERVICE_ACCOUNT or GOOGLE_APPLICATION_CREDENTIALS)"
        ),
    ),
]

@pytest.mark.integration
def test_build_app_initializes_and_registers_tools():
    # Arrange
    from myapp.wiring import build_app

    # Act
    app = build_app()

    # Assert
    assert hasattr(app, "run")

Test Isolation

Each test should be independent and not share state:

def test_user_created_in_one_test_not_visible_in_another(fake_auth, fake_users_repo):
    # Arrange
    svc1 = UserService(fake_auth, fake_users_repo)

    # Act
    result1 = svc1.add_user(email="test1@example.com", password="secret", name="User1")

    # Assert - second test with fresh fixtures should not see this user
    svc2 = UserService(fake_auth, fake_users_repo)
    users = svc2.list_users()
    assert users["count"] == 1  # Only the user from this test

Testing Anti-Patterns to Avoid

Don't Mock What You Don't Own

❌ Bad - Mocking external library:

@patch('firebase_admin.auth.create_user')
def test_add_user(mock_create_user):
    mock_create_user.return_value = Mock(uid="uid-1")
    # ... test code

✅ Good - Use Fake for your interface:

def test_add_user(fake_auth, fake_users_repo):
    svc = UserService(fake_auth, fake_users_repo)
    # ... test code

Don't Test Implementation Details

❌ Bad - Testing internal method calls:

def test_add_user(fake_auth, fake_users_repo):
    svc = UserService(fake_auth, fake_users_repo)
    svc.add_user(email="test@example.com", password="secret", name="User")
    assert fake_auth.created == [{"uid": "uid-1", ...}]  # Implementation detail

✅ Good - Testing observable behavior:

def test_add_user(fake_auth, fake_users_repo):
    svc = UserService(fake_auth, fake_users_repo)
    result = svc.add_user(email="test@example.com", password="secret", name="User")
    assert result["status"] == "ok"
    assert result["user"]["email"] == "test@example.com"

Don't Skip Error Paths

❌ Bad - Only happy path:

def test_add_user_success(fake_auth, fake_users_repo):
    # Only tests success case

✅ Good - Test all scenarios:

def test_add_user_success(fake_auth, fake_users_repo):
    # Happy path

def test_add_user_rollback_on_firestore_failure(fake_auth, fake_users_repo):
    # Error path

def test_add_user_handles_duplicate_email(fake_auth, fake_users_repo):
    # Edge case

Running Tests

# Run all tests
pytest

# Run with coverage
pytest --cov=myapp --cov-report=term-missing

# Run specific test file
pytest tests/test_user_service.py

# Run specific test
pytest tests/test_user_service.py::test_add_user_success

# Run parametrized tests with verbose output
pytest -v tests/test_user_service.py::test_add_user_parametrized

# Skip integration tests
pytest -m "not integration"

# Run only integration tests
pytest -m integration

# Stop on first failure
pytest -x

# Show local variables on failure
pytest -l

# Run tests in parallel (with pytest-xdist)
pytest -n auto

Best Practices Checklist

  • [ ] Use Fake classes instead of unittest.mock
  • [ ] Structure tests with explicit AAA comments
  • [ ] Use fixtures for test setup
  • [ ] Inject dependencies between fixtures
  • [ ] Parametrize tests for multiple scenarios
  • [ ] Test happy paths and error paths
  • [ ] Test edge cases and boundaries
  • [ ] Write contract tests for interfaces
  • [ ] Ensure test isolation
  • [ ] Use descriptive test names
  • [ ] Keep tests focused on one behavior
  • [ ] Avoid testing implementation details
  • [ ] Test at appropriate level (unit vs integration)
  • [ ] Mock external dependencies appropriately
  • [ ] Maintain test coverage

平台分布

OpenClaw

79.97%
按下载量换算7,024

安全审计

VirusTotal

通过

ClawScan

通过

Static analysis

未展示

权限和风险

调用外部服务

该 Skill 可能调用第三方服务、云服务或外部模型 API,使用前需要确认账号、额度、数据发送范围和服务条款。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。目前只有一个公开来源,建议同时核对源仓库。

继续浏览同类 Skills