Problem
tests/player_fake.py defines the Player test fake as a plain class with an
eleven-parameter __init__ that assigns each argument to self. This is the
pattern a C#/Java developer reaches for (a POCO/POJO with an explicit
constructor), but in Python it is boilerplate the standard library generates
for you.
Two consequences:
- Adding or renaming a player field means editing the parameter list, the
assignments, and (potentially) call sites by hand.
- Tests serialise the fake with
player.__dict__ (e.g. client.post(PATH, json=player.__dict__)), reaching into a dunder attribute instead of a
documented API.
Proposed Solution
Convert Player to a @dataclass. The decorator generates __init__,
__repr__, and __eq__ from the annotated fields. Replace player.__dict__
with dataclasses.asdict(player), the supported way to get a plain dict from a
dataclass instance.
from dataclasses import dataclass
@dataclass
class Player:
"""Test fake representing a Player."""
id: str | None = None
first_name: str | None = None
middle_name: str | None = None
last_name: str | None = None
date_of_birth: str | None = None
squad_number: int | None = None
position: str | None = None
abbr_position: str | None = None
team: str | None = None
league: str | None = None
starting11: bool | None = None
The factory functions (existing_player(), nonexistent_player(),
unknown_player()) already use keyword arguments, so they stay unchanged. In
tests/test_main.py and tests/conftest.py, swap player.__dict__ for
asdict(player) — both produce the same flat, snake_case dict, so request
payloads do not change.
Suggested Approach
- Add
from dataclasses import dataclass; decorate Player; delete the
explicit __init__.
- Annotate every field with
T | None and a = None default.
- Replace
player.__dict__ with asdict(player) in tests/test_main.py and
tests/conftest.py (from dataclasses import asdict).
- Run
uv run pytest — all tests must stay green.
Acceptance Criteria
References
Problem
tests/player_fake.pydefines thePlayertest fake as a plain class with aneleven-parameter
__init__that assigns each argument toself. This is thepattern a C#/Java developer reaches for (a POCO/POJO with an explicit
constructor), but in Python it is boilerplate the standard library generates
for you.
Two consequences:
assignments, and (potentially) call sites by hand.
player.__dict__(e.g.client.post(PATH, json=player.__dict__)), reaching into a dunder attribute instead of adocumented API.
Proposed Solution
Convert
Playerto a@dataclass. The decorator generates__init__,__repr__, and__eq__from the annotated fields. Replaceplayer.__dict__with
dataclasses.asdict(player), the supported way to get a plain dict from adataclass instance.
The factory functions (
existing_player(),nonexistent_player(),unknown_player()) already use keyword arguments, so they stay unchanged. Intests/test_main.pyandtests/conftest.py, swapplayer.__dict__forasdict(player)— both produce the same flat, snake_case dict, so requestpayloads do not change.
Suggested Approach
from dataclasses import dataclass; decoratePlayer; delete theexplicit
__init__.T | Noneand a= Nonedefault.player.__dict__withasdict(player)intests/test_main.pyandtests/conftest.py(from dataclasses import asdict).uv run pytest— all tests must stay green.Acceptance Criteria
Playeris a@dataclasswith no hand-written__init__T | Nonesyntax.__dict__access remains in the test suiteCHANGELOG.mdupdatedReferences
dataclasses— Python docsdataclasses.asdict