Decorator API¶
The @freeze_uuid decorator provides a clean way to configure UUID mocking at the function or class level.
Version-Specific Decorators¶
Use the version-specific decorators for clarity and to enable version-specific features:
| Decorator | UUID Version | Extra Parameters |
|---|---|---|
@freeze_uuid4(...) |
uuid4 (recommended) | - |
@freeze_uuid1(...) |
uuid1 | node, clock_seq |
@freeze_uuid6(...) |
uuid6 | node, clock_seq |
@freeze_uuid7(...) |
uuid7 | - |
@freeze_uuid8(...) |
uuid8 | - |
@freeze_uuid(...) |
uuid4 (backward compatibility) | - |
import uuid
from pytest_uuid import freeze_uuid4, freeze_uuid1, freeze_uuid7
@freeze_uuid4("12345678-1234-4678-8234-567812345678")
def test_uuid4():
assert str(uuid.uuid4()) == "12345678-1234-4678-8234-567812345678"
@freeze_uuid1(seed=42, node=0x123456789abc)
def test_uuid1_with_node():
result = uuid.uuid1()
assert result.node == 0x123456789abc
@freeze_uuid7(seed=42)
def test_uuid7():
result = uuid.uuid7()
assert result.version == 7
Stacking Multiple Versions¶
Mock multiple UUID versions in the same test:
@freeze_uuid4("aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa")
@freeze_uuid1(seed=42)
def test_multiple_versions():
assert str(uuid.uuid4()) == "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa"
result1 = uuid.uuid1()
assert result1.version == 1
Basic Usage¶
import uuid
from pytest_uuid import freeze_uuid
@freeze_uuid("12345678-1234-4678-8234-567812345678")
def test_with_decorator():
assert str(uuid.uuid4()) == "12345678-1234-4678-8234-567812345678"
Multiple UUIDs¶
Pass multiple UUIDs as a list:
@freeze_uuid([
"11111111-1111-4111-8111-111111111111",
"22222222-2222-4222-8222-222222222222",
])
def test_sequence():
assert str(uuid.uuid4()) == "11111111-1111-4111-8111-111111111111"
assert str(uuid.uuid4()) == "22222222-2222-4222-8222-222222222222"
Seeded UUIDs¶
Use a seed for reproducible UUIDs:
Custom Random Generator¶
Pass your own random.Random instance:
import random
from pytest_uuid import freeze_uuid
rng = random.Random(42)
rng.random() # Advance the state
@freeze_uuid(seed=rng)
def test_custom_rng():
# Gets UUIDs from the pre-advanced random state
result = uuid.uuid4()
Exhaustion Behavior¶
Control what happens when UUIDs are exhausted:
import uuid
import pytest
from pytest_uuid import freeze_uuid, UUIDsExhaustedError
@freeze_uuid(
["11111111-1111-4111-8111-111111111111"],
on_exhausted="raise",
)
def test_exhaustion():
uuid.uuid4() # OK
with pytest.raises(UUIDsExhaustedError):
uuid.uuid4() # Raises
Options:
"cycle"(default): Loop back to the start"random": Generate random UUIDs"raise": RaiseUUIDsExhaustedError
Ignoring Modules¶
Exclude modules from mocking:
@freeze_uuid("12345678-1234-4678-8234-567812345678", ignore=["sqlalchemy"])
def test_with_ignored():
# Direct calls are mocked
assert str(uuid.uuid4()) == "12345678-1234-4678-8234-567812345678"
# Calls from sqlalchemy get real UUIDs
Opting Out of Default Ignores¶
By default, certain packages (like botocore) are always ignored. Use ignore_defaults=False to mock all modules:
@freeze_uuid("12345678-1234-4678-8234-567812345678", ignore_defaults=False)
def test_mock_everything():
# All uuid.uuid4() calls are mocked, including from botocore
pass
Combine with ignore to replace the default list entirely:
@freeze_uuid(
"12345678-1234-4678-8234-567812345678",
ignore=["myapp.internal"],
ignore_defaults=False, # Don't include botocore
)
def test_custom_ignore():
# Only myapp.internal is ignored (not botocore)
pass
Class-Level Decorator¶
Apply to all methods in a test class:
@freeze_uuid("12345678-1234-4678-8234-567812345678")
class TestUserService:
def test_create(self):
assert str(uuid.uuid4()) == "12345678-1234-4678-8234-567812345678"
def test_update(self):
assert str(uuid.uuid4()) == "12345678-1234-4678-8234-567812345678"
Context Manager¶
Use freeze_uuid as a context manager for fine-grained control:
def test_context_manager():
with freeze_uuid("12345678-1234-4678-8234-567812345678"):
assert str(uuid.uuid4()) == "12345678-1234-4678-8234-567812345678"
# Original uuid.uuid4 is restored
assert uuid.uuid4() != uuid.UUID("12345678-1234-4678-8234-567812345678")
Access the freezer inside the context:
def test_with_freezer():
with freeze_uuid("12345678-1234-4678-8234-567812345678") as freezer:
uuid.uuid4()
freezer.reset() # Reset mid-test
uuid.uuid4()
Inspecting the Seed Value¶
When using seeded generation, access the seed property to see the actual seed:
def test_inspect_seed():
with freeze_uuid(seed=42) as freezer:
assert freezer.seed == 42
# With node-based seeding, see the computed seed
with freeze_uuid(seed="node", node_id="test.py::test_foo") as freezer:
print(f"Computed seed: {freezer.seed}") # e.g., 8427193654
Note
The seed property returns None when using static UUIDs, sequences,
or when a random.Random instance was passed directly.
Parameters¶
| Parameter | Type | Description |
|---|---|---|
uuids |
str, UUID, or sequence |
UUID(s) to return |
seed |
int, Random, or "node" |
Seed for reproducible generation |
on_exhausted |
str |
"cycle", "random", or "raise" |
ignore |
list[str] |
Module prefixes to exclude from mocking |
ignore_defaults |
bool |
Include default ignore list (default True) |
node |
int |
Fixed MAC address for uuid1/uuid6 |
clock_seq |
int |
Fixed clock sequence for uuid1/uuid6 |