macalbert/envilder/python-test-doubles

Claude Code and agent skill by macalbert/envilder

Trust tier 2
  • community
LLM review pending
Source check pending

Checking that the SKILL.md file is still in the repository.

Test doubles with unittest.mock (Mock, AsyncMock, patch) and Mother pattern. Use when creating test data, mocking dependencies, or setting up fixtures in Python tests.

Install

npx vskill@latest install macalbert/envilder/python-test-doubles

SKILL.md

Test Doubles (Python)

This skill defines how to use test doubles in Python (unittest.mock + pytest).


Test Doubles Types

1. Mock: Mock / AsyncMock

Use Mock and AsyncMock to create mock objects for protocol interfaces.

Purpose: Replace real dependencies with controllable test doubles.

python
from unittest.mock import Mock, AsyncMock
@pytest.fixture()
def secret_provider() -> Mock:
provider = Mock(spec=ISecretProvider)
provider.get_secret = Mock(return_value=None)
return provider
@pytest.fixture()
def async_provider() -> AsyncMock:
provider = AsyncMock(spec=ISecretProvider)
provider.get_secret = AsyncMock(return_value=None)
return provider

MANDATORY: Always use spec=InterfaceClass to catch typos at test time.

2. Stub: return_value / side_effect

Use return_value or side_effect to configure stubs.

Purpose: Predefined responses without caring about call verification.

python
# Simple stub
provider.get_secret.return_value = "secret-value"
# Sequential returns
provider.get_secret.side_effect = ["first", "second", None]
# Conditional stub
def resolve_secret(name: str) -> str | None:
secrets = {"/app/db": "postgres://...", "/app/key": "abc123"}
return secrets.get(name)
provider.get_secret.side_effect = resolve_secret
3. Spy: wraps

Use wraps to observe calls on real objects without replacing behavior.

Purpose: Verify interactions while preserving real implementation.

python
real_parser = MapFileParser()
spy_parser = Mock(wraps=real_parser)
sut = EnvilderClient(provider, spy_parser)
sut.resolve_secrets(map_file)
spy_parser.parse.assert_called_once_with(map_file)
4. Module Patch: patch / patch.object

Use patch to replace module-level objects or class methods.

Purpose: Replace external dependencies (boto3, file I/O, etc.).

python
from unittest.mock import patch
@patch("boto3.client")
def Should_CallSSM_When_AwsProviderUsed(mock_boto: Mock) -> None:
# Arrange
mock_ssm = Mock()
mock_boto.return_value = mock_ssm
mock_ssm.get_parameter.return_value = {"Parameter": {"Value": "secret"}}
# Act
sut = AwsSsmSecretProvider()
actual = sut.get_secret("/app/key")
# Assert
assert actual == "secret"
mock_ssm.get_parameter.assert_called_once()
5. Error Simulation: side_effect with Exception

Use side_effect with an exception to simulate failures.

Purpose: Test error paths and exception handling.

python
provider.get_secret.side_effect = ClientError(
{"Error": {"Code": "ParameterNotFound"}}, "GetParameter"
)

Verification Patterns

Basic Verification
python
provider.get_secret.assert_called_once_with("/ssm/path")
provider.get_secret.assert_called_with("/ssm/path")
logger.info.assert_called_once()
Not Called
python
provider.get_secret.assert_not_called()
logger.error.assert_not_called()
Call Count
python
assert provider.get_secret.call_count == 3
Argument Inspection
python
from unittest.mock import call
provider.get_secret.assert_has_calls([
call("/app/db"),
call("/app/key"),
], any_order=True)
Async Verification
python
provider.get_secret.assert_awaited_once_with("/ssm/path")
provider.get_secret.assert_awaited()
provider.get_secret.assert_not_awaited()

Pytest Fixtures as Factories

Use fixtures to build test doubles with proper lifecycle:

python
@pytest.fixture()
def secret_provider() -> Mock:
provider = Mock(spec=ISecretProvider)
provider.get_secret.return_value = None
return provider
@pytest.fixture()
def logger() -> Mock:
return Mock(spec=ILogger)
@pytest.fixture()
def sut(secret_provider: Mock, logger: Mock) -> EnvilderClient:
return EnvilderClient(provider=secret_provider, logger=logger)

Mother Pattern

Use factory functions or classes for reusable test data:

python
from dataclasses import dataclass
from typing import Optional
from uuid import UUID, uuid4
class MapFileMother:
@staticmethod
def create(
provider: str = "aws",
mappings: Optional[dict[str, str]] = None,
) -> ParsedMapFile:
return ParsedMapFile(
config=MapFileConfig(provider=provider),
mappings=mappings or {"DB_URL": "/app/db"},
)
class EnvilderOptionsMother:
@staticmethod
def create(
provider: SecretProviderType = SecretProviderType.AWS,
profile: Optional[str] = None,
vault_url: Optional[str] = None,
) -> EnvilderOptions:
return EnvilderOptions(
provider=provider,
profile=profile,
vault_url=vault_url,
)

Usage:

python
# Arrange
map_file = MapFileMother.create(mappings={"API_KEY": "/prod/key"})

Summary

Double TypePython APIPurpose
MockMock(spec=X)Controllable replacement
Stub.return_value / .side_effectPredefined responses
SpyMock(wraps=real)Observe real objects
Patch@patch("module.obj")Replace module-level deps
Error sim.side_effect = Exception(...)Failure paths

When writing tests:

  1. Create port mocks with Mock(spec=Interface) in fixtures
  2. Configure stubs with .return_value in Arrange
  3. Always verify mock interactions in Assert
  4. Use Mother pattern for complex test data
  5. Always use `spec=` to catch typo bugs
  6. Prefer fixture injection over inline Mock() creation

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewNot runA model review is scheduled for the next rescan.

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
135
Repository forks
4
7-day trend score
25

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/macalbert/envilder/python-test-doubles/badge)](https://verified-skill.com/skills/macalbert/envilder/python-test-doubles)

Something wrong with this skill or its page? Tell us.