Skip to main content

Expiration Policy

Expiration policies in HTTP mocking allow developers to simulate how an API might respond differently over time or after a certain number of requests. This feature is particularly useful in testing scenarios where the behavior of an API changes, such as rate limiting, temporary outages, or conditional responses based on request count.

ExpireAfterRequests Policy

The ExpireAfterRequests policy enables a mocked response to expire after a specified number of requests. Once expired, any subsequent requests will not match the mocked response, potentially triggering a different behavior or response as defined in your testing setup.

Basic Example

Consider the following basic example where we want to test how our application handles a scenario where an endpoint /users is available on the first request but becomes unavailable on the second request.

import httpx
import jj
from jj.expiration_policy import ExpireAfterRequests

matcher = jj.match("*", "/users")
response = jj.Response(status=200, json=[])

# Mock the endpoint with an expiration policy
with mocked(matcher, response, expiration_policy=ExpireAfterRequests(1)):
# First request matches and gets a 200 response
res1 = httpx.get("http://localhost:8080/users")
# Second request does not match as the mock has expired, resulting in a 404
res2 = httpx.get("http://localhost:8080/users")

assert res1.status_code == 200
assert res2.status_code == 404

In this example, res1 receives a 200 status code because it matches the mocked response. However, res2 receives a 404 status code because the mock expired after the first request.

Advanced Use Cases

The ExpireAfterRequests policy can be used to simulate more complex scenarios, such as an API endpoint that temporarily fails and then recovers.

To simulate an endpoint that fails on the first request but succeeds on subsequent requests:

import httpx
import jj
from jj.expiration_policy import ExpireAfterRequests

# Define a matcher for the endpoint
matcher = jj.match("*", "/users")

# Define the expiration policy
policy = ExpireAfterRequests(1)

# Create responses for failure and success scenarios
response1 = jj.Response(status=500, json={"error": "message"})
response2 = jj.Response(status=200, json=[{"id": 1}])

# Mock the endpoint with the responses and expiration policy
with mocked(matcher, response2, expiration_policy=policy):
with mocked(matcher, response1, expiration_policy=policy):
# First request triggers the failure response
res1 = httpx.get("http://localhost:8080/users")
# Second request matches the success response
res2 = httpx.get("http://localhost:8080/users")

# Verify the responses
assert res1.status_code == 500
assert res2.status_code == 200

Note: When using nested with mocked statements, the inner with mocked takes precedence over the outer one. This allows for more specific mocking behavior within a broader mocking context, as demonstrated in the example above where the first request is intentionally failed with a 500 status code before succeeding with a 200 on the subsequent request.