Fixture API¶
The fixture API provides the most flexibility for controlling UUID generation in your tests.
mock_uuid Fixture¶
The main fixture for controlling uuid.uuid4() calls.
Basic Usage¶
import uuid
def test_basic(mock_uuid):
mock_uuid.uuid4.set("12345678-1234-4678-8234-567812345678")
assert str(uuid.uuid4()) == "12345678-1234-4678-8234-567812345678"
Static UUIDs¶
Return the same UUID every time:
def test_static(mock_uuid):
mock_uuid.uuid4.set("12345678-1234-4678-8234-567812345678")
assert uuid.uuid4() == uuid.uuid4() # Same UUID
UUID Sequences¶
Return UUIDs from a list:
def test_sequence(mock_uuid):
mock_uuid.uuid4.set(
"11111111-1111-4111-8111-111111111111",
"22222222-2222-4222-8222-222222222222",
)
assert str(uuid.uuid4()) == "11111111-1111-4111-8111-111111111111"
assert str(uuid.uuid4()) == "22222222-2222-4222-8222-222222222222"
# Cycles back by default
assert str(uuid.uuid4()) == "11111111-1111-4111-8111-111111111111"
Seeded UUIDs¶
Generate reproducible UUIDs from a seed:
def test_seeded(mock_uuid):
mock_uuid.uuid4.set_seed(42)
first = uuid.uuid4()
mock_uuid.uuid4.set_seed(42) # Reset to same seed
assert uuid.uuid4() == first # Same UUID
Node-Seeded UUIDs¶
Derive the seed from the test's node ID:
def test_node_seeded(mock_uuid):
mock_uuid.uuid4.set_seed_from_node()
# Same test always produces the same sequence
Inspecting the Seed Value¶
Use the seed property to see the actual seed being used:
def test_inspect_seed(mock_uuid):
mock_uuid.uuid4.set_seed(42)
assert mock_uuid.uuid4.seed == 42
def test_inspect_node_seed(mock_uuid):
mock_uuid.uuid4.set_seed_from_node()
# See the computed seed derived from the test's node ID
print(f"Using seed: {mock_uuid.uuid4.seed}") # e.g., 8427193654
Debugging reproducibility
The seed property is useful for debugging. If a test fails, log the seed value
so you can reproduce the exact sequence later by passing that seed to set_seed().
Exhaustion Behavior¶
Control what happens when a UUID sequence is exhausted:
import uuid
import pytest
from pytest_uuid import UUIDsExhaustedError
def test_exhaustion_raise(mock_uuid):
mock_uuid.uuid4.set_exhaustion_behavior("raise")
mock_uuid.uuid4.set("11111111-1111-4111-8111-111111111111")
uuid.uuid4() # Returns the UUID
with pytest.raises(UUIDsExhaustedError):
uuid.uuid4() # Raises - sequence exhausted
Exhaustion behaviors:
"cycle"(default): Loop back to the start of the sequence"random": Fall back to generating random UUIDs"raise": RaiseUUIDsExhaustedError
Ignoring Modules¶
Exclude specific packages from UUID mocking:
def test_with_ignored_modules(mock_uuid):
mock_uuid.uuid4.set("12345678-1234-4678-8234-567812345678")
mock_uuid.uuid4.set_ignore("sqlalchemy", "celery")
# Direct calls are mocked
assert str(uuid.uuid4()) == "12345678-1234-4678-8234-567812345678"
# Calls from sqlalchemy/celery get real UUIDs
How ignore works
The ignore check inspects the entire call stack, not just the immediate caller. If any frame in the call chain is from an ignored module, real UUIDs are returned.
Reset¶
Reset the fixture to its initial state:
def test_reset(mock_uuid):
mock_uuid.uuid4.set("12345678-1234-4678-8234-567812345678")
uuid.uuid4()
mock_uuid.uuid4.reset()
# Back to initial state - no UUIDs configured
Additional UUID Versions¶
The mock_uuid fixture provides access to sub-mockers for all UUID versions via properties.
UUID1 (Time-based with MAC)¶
import uuid
def test_uuid1_mocked(mock_uuid):
mock_uuid.uuid1.set("12345678-1234-1234-8234-567812345678")
assert str(uuid.uuid1()) == "12345678-1234-1234-8234-567812345678"
def test_uuid1_seeded(mock_uuid):
mock_uuid.uuid1.set_seed(42)
first = uuid.uuid1()
mock_uuid.uuid1.reset()
mock_uuid.uuid1.set_seed(42)
assert uuid.uuid1() == first
def test_uuid1_fixed_node(mock_uuid):
# Return real uuid1 values with a fixed node (MAC address)
mock_uuid.uuid1.set_node(0x123456789ABC)
result = uuid.uuid1()
assert result.node == 0x123456789ABC
UUID3 and UUID5 (Namespace-based)¶
UUID3 (MD5) and UUID5 (SHA-1) are deterministic, so they only support spy mode:
import uuid
def test_uuid3_tracking(mock_uuid):
_ = mock_uuid.uuid3 # Initialize spy
result = uuid.uuid3(uuid.NAMESPACE_DNS, "example.com")
assert mock_uuid.uuid3.call_count == 1
assert mock_uuid.uuid3.calls[0].namespace == uuid.NAMESPACE_DNS
assert mock_uuid.uuid3.calls[0].name == "example.com"
def test_uuid5_filtering(mock_uuid):
_ = mock_uuid.uuid5
uuid.uuid5(uuid.NAMESPACE_DNS, "example.com")
uuid.uuid5(uuid.NAMESPACE_URL, "https://example.com")
dns_calls = mock_uuid.uuid5.calls_with_namespace(uuid.NAMESPACE_DNS)
assert len(dns_calls) == 1
UUID6, UUID7, UUID8 (RFC 9562)¶
These require Python 3.14+ or the uuid6 package:
from uuid6 import uuid6, uuid7, uuid8 # or from uuid import ... on Python 3.14+
def test_uuid7_mocked(mock_uuid):
mock_uuid.uuid7.set("12345678-1234-7234-8234-567812345678")
result = uuid7()
assert str(result) == "12345678-1234-7234-8234-567812345678"
def test_uuid7_seeded(mock_uuid):
mock_uuid.uuid7.set_seed(42)
first = uuid7()
mock_uuid.uuid7.reset()
mock_uuid.uuid7.set_seed(42)
assert uuid7() == first
Independent Call Tracking¶
Each UUID version is tracked independently:
def test_all_versions_independent(mock_uuid):
mock_uuid.uuid4.set("44444444-4444-4444-8444-444444444444") # uuid4
mock_uuid.uuid1.set("11111111-1111-1111-8111-111111111111")
uuid.uuid4()
uuid.uuid4()
uuid.uuid1()
assert mock_uuid.uuid4.call_count == 2 # uuid4 only
assert mock_uuid.uuid1.call_count == 1
mock_uuid_factory Fixture¶
For module-specific mocking:
# myapp/models.py
from uuid import uuid4
def create_user():
return {"id": str(uuid4()), "name": "John"}
# tests/test_models.py
def test_create_user(mock_uuid_factory):
with mock_uuid_factory("myapp.models") as mocker:
mocker.uuid4.set("12345678-1234-4678-8234-567812345678")
user = create_user()
assert user["id"] == "12345678-1234-4678-8234-567812345678"
Mocking Default-Ignored Packages¶
By default, packages like botocore are ignored. Use ignore_defaults=False to mock them:
def test_mock_botocore(mock_uuid_factory):
with mock_uuid_factory("botocore.handlers", ignore_defaults=False) as mocker:
mocker.uuid4.set("12345678-1234-4678-8234-567812345678")
# botocore will now receive mocked UUIDs
Methods Reference¶
UUIDMocker (container)¶
| Property/Method | Description |
|---|---|
uuid4 |
Access UUID4Mocker for uuid.uuid4() |
uuid1 |
Access UUID1Mocker for uuid.uuid1() |
uuid3 |
Access NamespaceUUIDSpy for uuid.uuid3() |
uuid5 |
Access NamespaceUUIDSpy for uuid.uuid5() |
uuid6 |
Access UUID6Mocker for uuid.uuid6() |
uuid7 |
Access UUID7Mocker for uuid.uuid7() |
uuid8 |
Access UUID8Mocker for uuid.uuid8() |
reset() |
Reset all initialized sub-mockers |
UUID4Mocker (accessed via mock_uuid.uuid4)¶
| Method | Description |
|---|---|
set(*uuids) |
Set one or more UUIDs to return (cycles by default) |
set_default(uuid) |
Set a default UUID for all calls |
set_seed(seed) |
Set a seed for reproducible generation |
set_seed_from_node() |
Use test node ID as seed |
set_exhaustion_behavior(behavior) |
Set behavior when sequence exhausted |
spy() |
Switch to spy mode (return real UUIDs while tracking) |
reset() |
Reset to initial state |
set_ignore(*module_prefixes) |
Set modules to ignore |