Testing¶
Code that manages ads moves real money, so it is worth testing without touching the API.
A fake session¶
Client accepts any object with a request() method as its session, so a stand-in is a few lines:
import json
class FakeResponse:
def __init__(self, body, status_code=200, headers=None):
self._body = body
self.status_code = status_code
self.headers = headers or {}
self.text = json.dumps(body)
def json(self):
return self._body
def ok(result=True):
return FakeResponse({"ok": True, "result": result})
def fail(code):
return FakeResponse({"ok": False, "error": code})
class FakeSession:
def __init__(self, *responses):
self.responses = list(responses)
self.requests = []
def request(self, method, url, **kwargs):
self.requests.append({"url": url, **kwargs})
return self.responses.pop(0) if self.responses else ok()
def close(self):
pass
Every call is a POST to <base_url>/<method>; the parameters are in kwargs["json"] (or kwargs["data"] and kwargs["files"] for uploads), and the idempotency key, when there is one, is in kwargs["headers"]["Idempotency-Key"].
Asserting what your code sends¶
from telead import Client
def pause_overspent(client):
for ad in client.iter_ads():
if ad.daily_budget_limit and ad.daily_spent_budget >= ad.daily_budget_limit:
client.pause_ad(ad.ad_id)
def test_pauses_only_overspent_ads():
session = FakeSession(
ok({"total_count": 2, "ads": [
{"ad_id": 1, "daily_budget_limit": 10, "daily_spent_budget": 10},
{"ad_id": 2, "daily_budget_limit": 10, "daily_spent_budget": 3},
]}),
ok({"ad_id": 1, "is_paused": True}),
)
client = Client("test-token", session=session)
pause_overspent(client)
assert [r["url"].rsplit("/", 1)[1] for r in session.requests] == ["getAdsList", "editAd"]
assert session.requests[1]["json"] == {"ad_id": 1, "is_paused": True}
Testing error paths¶
Queue a failure the same way:
import pytest
from telead import Client, NotEnoughBudgetError
def test_top_up_stops_when_main_account_is_empty():
session = FakeSession(fail("NOT_ENOUGH_BUDGET"))
client = Client("test-token", session=session)
with pytest.raises(NotEnoughBudgetError):
client.increase_account_budget("rel-1", 100)
Keeping retries out of the way¶
Retries sleep between attempts. Turn them off in tests, or patch the sleep:
@pytest.fixture(autouse=True)
def no_sleep(monkeypatch):
monkeypatch.setattr("telead.client.time.sleep", lambda seconds: None)
Building models directly¶
Every model has from_dict, taking the API's JSON:
from telead import Ad
ad = Ad.from_dict({"ad_id": 1, "status": "declined",
"decline_reason": {"text": "Misleading", "description_html": "…"}})
assert ad.is_declined
The library's own tests¶
They use the same technique — see tests/conftest.py.