Skip to content

Testing automation roadmap: CI, coverage, and the path beyond localhost tests #1278

@jonfroehlich

Description

@jonfroehlich

Summary

Tracking issue capturing a design discussion (June 2026) on maturing our testing setup. We started writing tests for the first time a few days ago (see #1267); the suite now lives in website/tests.py (~1,580 lines) with a solid two-tier design — SimpleTestCase + MagicMock for pure logic, DatabaseTestCase for the DB/view/template layer. The tests exist; nothing runs them automatically.

The core gap

We auto-deploy master to the test server on every push:

push to master → live on makeabilitylab-test.cs.washington.edu, with no checks in between.

So the regression tests we just wrote only protect us if a contributor remembers to run manage.py test in the container before pushing. With rotating student contributors and push-to-deploy, that honor-system gate is exactly what continuous integration (CI) removes.

Mechanics / cost (for the record)

  • GitHub Actions is free and unlimited for public repos (this repo is public). No billing, no minute caps. A run of our suite is ~1–3 min.
  • A failing test is a signal, not a gate: it shows a red ✗ on the commit and emails the author. It does not block, revert, or alter pushed code. Merge-blocking only happens if we opt into branch protection later.

Prioritized roadmap

  • 1. GitHub Actions CI — run manage.py test website on PRs (and optionally pushes to master) against a Postgres 16 service container. Highest leverage.Done in Add CI + test-settings shim + split tests into a package (#1279) #1280.
  • 2. Test-settings shimmakeabilitylab/settings_test.py with MIGRATION_MODULES = {'website': None} + --run-syncdb so the runner builds schema from models, bypassing the gitignored per-env migration history. Durable fix for the column already exists flakiness (Need much better testing infracture on local host #1267). Prerequisite for CI.Done in Add CI + test-settings shim + split tests into a package (#1279) #1280.
  • 3. Split tests.py into a tests/ packagetests/base.py (shared DatabaseTestCase + fixtures) plus test_*.py by concern. Django auto-discovers; pure move, no config. ✅ Done in Add CI + test-settings shim + split tests into a package (#1279) #1280.
  • 4. Coverage measurementcoverage.py (stay on Django's runner; no pytest). Report in CI, don't gate on a number yet — use it to target backfill.
  • 5. Backfill high-risk untested code — ranked: delete_unused_files.py (runs every container start, deletes files, two latent crashes already found); a parametrized view smoke-sweep (GET every public URL → 200, catches the NoReverseMatch/AttributeError template bug class); Person.save() side effects; pure utils (timeutils, ml_utils, fileutils).
  • 6. Pa11y in CI — we already have the service + .pa11yci.json; CONTRIBUTING requires it on UI changes but nothing enforces it. Wire into the workflow.
  • 7. JS unit tests — deferred. ~2,633 lines of vanilla JS (incl. a11y-critical lightbox.js), but a Jest/node harness adds the build step we deliberately avoid. Let Pa11y cover rendered a11y for now; revisit only if we want unit-level JS coverage.

First PR

Items 1 + 2 + 3 shipped together in #1279 / PR #1280 (merged 2026-06-15) — CI now runs the suite on every push to master and every PR.

Remaining work: items 4, 5, 6 (item 7 deferred). Each is a good standalone PR.

Related

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions