Skip to content

Add injectable delay strategy to MockVWS to remove test-time monkeypatching #2919

@adamtheturtle

Description

@adamtheturtle

Problem

MockVWS currently hardcodes time.sleep(...) inside _wrap_callback, which makes timeout-boundary tests in downstream projects slow and encourages monkeypatching internals.

Current behavior in mock_vws/_requests_mock_server/decorators.py:

  • if delay_seconds > timeout: time.sleep(timeout) then raise requests.exceptions.Timeout
  • else: callback + time.sleep(delay_seconds)

This is correct semantically, but not test-friendly.

Proposed solution (preferred)

Add a first-class, injectable delay function on MockVWS so tests can control virtual time without monkeypatching module internals.

API proposal

class MockVWS:
    def __init__(
        ...,
        response_delay_seconds: float = 0.0,
        sleep_fn: Callable[[float], None] = time.sleep,
    ) -> None:
        self._sleep_fn = sleep_fn

Then use self._sleep_fn(...) in _wrap_callback instead of direct time.sleep(...).

Optional enhancement

If needed, add clock_fn: Callable[[], float] = time.time for future elapsed-time-based delay logic.

Why this is better than an internal sleep loop

  • Keeps default behavior exactly the same for normal users.
  • Makes tests deterministic and fast without monkeypatching private module paths.
  • Avoids tight coupling to freezegun internals.
  • Works for both success and timeout branches.

Example downstream usage

with freeze_time() as frozen:
    mock = MockVWS(
        response_delay_seconds=31,
        sleep_fn=lambda s: frozen.tick(delta=datetime.timedelta(seconds=s)),
    )

Acceptance criteria

  1. MockVWS accepts sleep_fn with default time.sleep.
  2. Existing behavior remains unchanged when sleep_fn is not provided.
  3. Existing tests pass unchanged.
  4. Add one new test proving injected sleep_fn is called for both:
    • non-timeout delay path
    • timeout path (delay_seconds > effective timeout)
  5. Docs/changelog mention test-time virtual delay support.

Non-goals

  • No change to public timeout semantics.
  • No forced dependency on freezegun.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions