Skip to main content

Testing

The suite is pytest + pytest-django. Settings: EMAW_BACKEND.settings.test (development minus external services: locmem cache and email, eager Celery, no Redis needed).

One-time: a local PostgreSQL the suite may own​

Tests create and drop databases, which the shared dev role cannot do. Run a throwaway container once:

docker run -d --name emaw_test_pg `
-e POSTGRES_USER=emaw_test -e POSTGRES_PASSWORD=emaw_test -e POSTGRES_DB=emaw_test `
-p 127.0.0.1:5434:5432 postgres:16-alpine

Start it again later with docker start emaw_test_pg.

Run​

The TEST_DB_* variables replace the matching DB_* for the test process only. From Git Bash:

TEST_DB_HOST=127.0.0.1 TEST_DB_PORT=5434 TEST_DB_USER=emaw_test TEST_DB_PASSWORD=emaw_test TEST_DB_NAME=emaw_test \
.venv/Scripts/python.exe -m pytest

From PowerShell:

$env:TEST_DB_HOST="127.0.0.1"; $env:TEST_DB_PORT="5434"; $env:TEST_DB_USER="emaw_test"; $env:TEST_DB_PASSWORD="emaw_test"; $env:TEST_DB_NAME="emaw_test"
.venv\Scripts\python.exe -m pytest

Useful variants:

python -m pytest crm -k mailable # one app, one keyword
python -m pytest --create-db # after schema changes (--reuse-db is on by default)
python -m pytest -x -q # stop at first failure

Layout​

ItemWhere
Configpytest.ini: testpaths = crm campaigns EMAW events resources store donations payments shipping deposco notes bibles ai, addopts = --reuse-db -q, python_files = test_*.py
Shared fixturesroot conftest.py: plain_user, superuser, crm_staff_user, contact, api_client, auth_client (issues a real SimpleJWT bearer token), and an autouse fixture that patches Stripe card/subscription/invoice lookups
Factories<app>/tests/factories.py (factory-boy); HTTP mocking with responses
Tests<app>/tests/test_*.py

Every ticket ships tests for its own code in its app's tests/ folder.

Coverage today​

AppTest files
crm31
store15
campaigns12
EMAW10
events9
deposco6
donations5
resources4
shipping3
payments2
notes, bibles, ai0 (in testpaths, nothing written yet)

The three empty apps are on the known gaps list.

Writing a test​

import pytest
from crm.tests.factories import ContactFactory

@pytest.mark.django_db
def test_contact_list_requires_capability(auth_client, plain_user):
client = auth_client(plain_user)
response = client.get("/rest/crm/contacts/")
assert response.status_code == 403

@pytest.mark.django_db
def test_staff_sees_contacts(auth_client, crm_staff_user):
ContactFactory(first_name="Ada")
client = auth_client(crm_staff_user)
response = client.get("/rest/crm/contacts/")
assert response.status_code == 200
assert response.json()["count"] == 1

Rules of thumb:

  • Hit endpoints through auth_client, not view functions, so permissions and throttles are exercised.
  • Mock Stripe, Deposco, ACS, Graph, OpenAI and Azure Maps at the HTTP boundary (responses) or at the client module; never let a test reach the network.
  • Celery is eager in test settings, so tasks run inline; assert on their effects.

Root scratch scripts​

verify_redis_down.py, verify_redis_opt.py and verify_seed_dupes.py in the repo root use settings/local_verify.py and a committed verify_db.sqlite3. They are manual checks, not part of the suite.