Goal
SkillDev toolsRemove mocking approach from unit tests
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Goal skill
What this skill tells your AI
The instructions your AI receives, as published by causify-ai/helpers in .claude/skills/testing.fix_mock_tests/SKILL.md and read by ahel’s review.
-
When the user passes a test file, apply the following transformations to remove mocking from unit testing code without changing behavior
-
Mocking often signals that your design could be more modular
- Avoiding mocking in Python unit tests means designing code so dependencies are easy to control naturally, rather than being intercepted artificially
-
Avoid mocking using the strategies below
Prefer Pure Functions
- Functions that depend only on inputs and return outputs are trivial to test
- Example:
def add(a: int, b: int) -> int: return a + b
- Example:
- No mocking needed because there are no external dependencies
Design for Dependency Injection (DI)
- Instead of hard coding dependencies inside functions or classes, pass them in
- Bad approach:
from typing import Any import requests def get_user(user_id: int) -> Any: return requests.get(f"https://api.com/users/{user_id}").json() - Good approach:
from typing import Any, Protocol class HttpClient(Protocol): def get(self, url: str) -> Any: ... def get_user(user_id: int, http_client: HttpClient) -> Any: return http_client.get(f"https://api.com/users/{user_id}").json() - Bad approach:
class Database: pass class UserService: def __init__(self) -> None: self.db: Database = Database() - Good approach:
class Database: pass class UserService: def __init__(self, db: Database) -> None: self.db: Database = db
Create Lightweight Fakes Instead of Mocks
- A fake is a real implementation with simplified behavior
- More maintainable and less brittle than mocks
- Example:
from typing import Any class FakeDatabase: def __init__(self) -> None: self.data: dict[str, Any] = {} def save(self, key: str, value: Any) -> None: self.data[key] = value def get(self, key: str) -> Any: return self.data.get(key)
Separate Side Effects From Logic
- Keep core logic independent from I/O
- Bad approach:
from typing import Any def process_user(user_id: int) -> None: user: Any = db.get(user_id) send_email(user) - Good approach:
from typing import Any, Callable def process_user(user: Any, send_email_fn: Callable[[Any], None]) -> None: send_email_fn(user) - Now you can test logic without mocking the database or email system
Test Behavior Via Public Interfaces
- Avoid testing internal calls (which often leads to mocking)
- Focus on outputs and state changes
- Verify the final result or state instead of verifying a method was called
Use Real Collaborators When Cheap
- If a dependency is fast and deterministic, just use it
- JSON parsing
- Simple utility classes
- Pure transformations
- Mocking these adds unnecessary complexity
Use Integration Tests Strategically
- Sometimes the best way to avoid mocking is not to unit test in isolation, but to test a small system working together
Important
- All invariants and conventions for unit tests are documented in
.claude/skills/testing.rules.md - For all code you must follow the instructions in
.claude/skills/coding.rules.md
Signals
- GitHub stars
- 145
- Forks
- 159
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
testing-fix-mock-tests- Source
- github.com/causify-ai/helpers