You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Adds a `Test shape` section to `docs/developer/backend.md`
between `Serializer shape` and `Auth`, capturing the per-resource
file layout, the factory-helpers pattern in `common.py`, and the
test_<subject>_<action>_<expectation> method-name form.
Updates the test section in `backend-docstring-style.md` to point
at the new layout and show the factory-helpers + setUp shape; the
behavioral-docstring guidance is unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: docs/developer/backend-docstring-style.md
+12-3Lines changed: 12 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -178,16 +178,25 @@ See [`backend/ctj_api/urls.py`](https://github.com/hackforla/CivicTechJobs/blob/
178
178
179
179
### Tests — light template, behavioral test names
180
180
181
-
Tests get a module docstring and a class docstring (one-liner each). Each test method's docstring is a behavioral statement of what the test verifies, not a label for the method.
181
+
Tests live one file per resource under [`backend/ctj_api/tests/`](https://github.com/hackforla/CivicTechJobs/tree/main/backend/ctj_api/tests) (see [`backend.md`](backend.md)'s `Test shape` section for the full layout rule). Each file holds one `<Resource>Tests` class extending `APITestCase`; shared fixture-construction lives in `common.py` as factory helpers.
182
+
183
+
Test method names follow `test_<subject>_<action>_<expectation>`. Each method's docstring is a behavioral statement of what the test verifies, not a label for the method.
182
184
183
185
```python
184
186
"""<one-liner: what this test module covers>."""
185
187
186
188
187
189
classFooTests(APITestCase):
188
-
"""<one-liner: what this test class scopes to>."""
190
+
"""<one-liner: what this test class scopes to>.
191
+
192
+
[Optional: prose paragraph for the resource's policy
193
+
surface, e.g. "Reads are public; mutations gated by ...".]
FBVs reference the right serializer directly — they only do one thing, so they only ever need one class.
117
117
118
+
## Test shape
119
+
120
+
Tests live in [backend/ctj_api/tests/](https://github.com/hackforla/CivicTechJobs/tree/main/backend/ctj_api/tests), one file per resource:
121
+
122
+
```
123
+
ctj_api/tests/
124
+
├── common.py — factory helpers, no test classes
125
+
├── test_healthcheck.py
126
+
├── test_users.py
127
+
├── test_opportunities.py
128
+
├── test_community_of_practice.py
129
+
├── test_roles.py
130
+
├── test_skills.py
131
+
└── test_projects.py
132
+
```
133
+
134
+
Each file holds one `<Resource>Tests` class extending `APITestCase`. Each class has its own `setUp` that constructs only the rows its tests need — there is no shared `APITestBase` because there is no meaningful universal setup (healthcheck needs nothing; user-detail needs users; opportunity tests need users + reference rows).
135
+
136
+
Shared fixture-construction lives in `common.py` as factory functions (`make_pm_user`, `make_regular_user`, `make_cop`, `make_role`, `make_skill`, `make_project`, `make_opportunity`). Each call returns a saved instance; defaults are sensible and overridable via keyword arguments. The factories don't share state — each call creates a new row.
137
+
138
+
Test method names follow `test_<subject>_<action>_<expectation>`:
139
+
140
+
- subject — who's making the request (`anonymous`, `regular_user`, `pm_user`, `authenticated_user`)
141
+
- action — what's being attempted (`list`, `create`, `view_own_record`)
142
+
- expectation — the result (`returns_200`, `cannot_create`, `gets_403`)
143
+
144
+
Examples: `test_anonymous_can_list_opportunities`, `test_regular_user_cannot_create_opportunity`, `test_authenticated_user_cannot_view_others_record`. Each test method also has a one-liner docstring describing the assertion in plain English.
145
+
146
+
When a resource grows write tests alongside read tests (and the file gets long), split into multiple classes within the same file: `<Resource>ReadTests`, `<Resource>WriteTests`. Don't split into separate files unless the test count justifies it.
147
+
118
148
## Auth
119
149
120
150
**Stage 1** - Django's default session authentication. The DRF API uses `SessionAuthentication`; the app frontend signs in through a Django-issued session cookie. `createsuperuser` is the bootstrap path for admin / PM accounts. Sessions over token auth here because Django admin already uses sessions, so reusing them keeps Stage 1 free of extra auth infrastructure.
0 commit comments