# Send Clean Pytest to your agent
Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.
## Fast path
- Download the package from Yavira.
- Extract it into a folder your agent can access.
- Paste one of the prompts below and point your agent at the extracted folder.
## Suggested prompts
### New install

```text
I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete.
```
### Upgrade existing

```text
I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run.
```
## Machine-readable fields
```json
{
  "schemaVersion": "1.0",
  "item": {
    "slug": "clean-pytest",
    "name": "Clean Pytest",
    "source": "tencent",
    "type": "skill",
    "category": "开发工具",
    "sourceUrl": "https://clawhub.ai/marcoracer/clean-pytest",
    "canonicalUrl": "https://clawhub.ai/marcoracer/clean-pytest",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadUrl": "/downloads/clean-pytest",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=clean-pytest",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "packageFormat": "ZIP package",
    "primaryDoc": "SKILL.md",
    "includedAssets": [
      "skill.json",
      "SKILL.md"
    ],
    "downloadMode": "redirect",
    "sourceHealth": {
      "source": "tencent",
      "slug": "clean-pytest",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-05-01T20:33:55.865Z",
      "expiresAt": "2026-05-08T20:33:55.865Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=clean-pytest",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=clean-pytest",
        "contentDisposition": "attachment; filename=\"clean-pytest-0.1.0.zip\"",
        "redirectLocation": null,
        "bodySnippet": null,
        "slug": "clean-pytest"
      },
      "scope": "item",
      "summary": "Item download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this item.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/clean-pytest"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    }
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/clean-pytest",
    "downloadUrl": "https://openagent3.xyz/downloads/clean-pytest",
    "agentUrl": "https://openagent3.xyz/skills/clean-pytest/agent",
    "manifestUrl": "https://openagent3.xyz/skills/clean-pytest/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/clean-pytest/agent.md"
  }
}
```
## Documentation

### 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

### 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)

### 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)

### 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)

### 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)

### 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

### 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"

### 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

### 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
## Trust
- Source: tencent
- Verification: Indexed source record
- Publisher: marcoracer
- Version: 0.1.0
## Source health
- Status: healthy
- Item download looks usable.
- Yavira can redirect you to the upstream package for this item.
- Health scope: item
- Reason: direct_download_ok
- Checked at: 2026-05-01T20:33:55.865Z
- Expires at: 2026-05-08T20:33:55.865Z
- Recommended action: Download for OpenClaw
## Links
- [Detail page](https://openagent3.xyz/skills/clean-pytest)
- [Send to Agent page](https://openagent3.xyz/skills/clean-pytest/agent)
- [JSON manifest](https://openagent3.xyz/skills/clean-pytest/agent.json)
- [Markdown brief](https://openagent3.xyz/skills/clean-pytest/agent.md)
- [Download page](https://openagent3.xyz/downloads/clean-pytest)