diff --git a/docs/testing.md b/docs/testing.md index 6e885cbf6d2..351a094deca 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -116,4 +116,8 @@ There are a lot of taskid, revisions, and expected fields to update in tests. F - ensure variety of platforms, builds, tests, pass/fail, etc. are included - push_data.json - adjust the dates to have multiple days (1st +1, 2nd +2, 3rd +3) +## Writing Tests + +For guidance on writing tests (conventions, examples, and recommended frameworks), see the separate guide: [Writing Tests](writing_tests.md). + [eslint]: https://eslint.org diff --git a/docs/writing_tests.md b/docs/writing_tests.md new file mode 100644 index 00000000000..270edd9dd78 --- /dev/null +++ b/docs/writing_tests.md @@ -0,0 +1,67 @@ +# Writing Tests + +## Purpose + +- Tests should be fast, deterministic, and focused. Prefer unit tests for logic, integration tests for component interactions, and end-to-end tests for critical workflows. + +## Frameworks we use + +- Python + - Test runner: `pytest` (with `pytest-django` for Django integration). + - Mocks: `responses` for HTTP responses; `pytest-mock` patterns also appear. + - Test data: fixtures under `tests/sample_data/`. + - Database tests: use `@pytest.mark.django_db`. +- Frontend + - Unit tests: `Jest` with React Testing Library. + - Integration: PollyJS recordings are used for integration tests to record/replay API interactions. + - End-to-end: `jest-puppeteer`/Puppeteer is available for e2e tests. + +## Guidelines + +- Test small units of logic first (pure functions, transformers). +- Use `responses` to mock external HTTP requests. +- Use `@pytest.mark.django_db` when interacting with the DB. +- Keep unit tests fast (<100ms each ideally). Group slow tests and run separately. +- For frontend components use React Testing Library; avoid testing implementation details. +- Use fixtures (`tests/conftest.py`) and shared sample data in `tests/sample_data/`. +- Freeze time where necessary or inject clock values (e.g., `freezegun`) to keep tests deterministic. + +## Examples + +- Mocking HTTP: + +```python +responses.add(responses.GET, "https://api.example", json={"ok": True}, status=200) +``` + +- Simple pytest fixture: + +```python +@pytest.fixture +def github_push(sample_data): + return copy.deepcopy(sample_data.github_push) +``` + +- DB test example: + +```python +@pytest.mark.django_db +def test_ingest_hg_push(...): + PushLoader().process(...) + assert Push.objects.count() == 1 +``` + +## Resources + +- [Pytest](https://docs.pytest.org/) +- [Pytest-Django](https://pytest-django.readthedocs.io/) +- [Responses](https://github.com/getsentry/responses) +- [React Testing Library](https://testing-library.com/) +- [Jest](https://jestjs.io/) +- [PollyJS](https://netflix.github.io/pollyjs/) + +## Best practices + +- Keep tests deterministic and mock external dependencies. +- Run `yarn test` and `pytest` locally in Docker as per `docs/testing.md`. +- Add tests for bug fixes and new public APIs. diff --git a/mkdocs.yml b/mkdocs.yml index 7464090748d..6eb80ca5ad8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -34,7 +34,9 @@ nav: - About: 'index.md' - Development: - Installation: 'installation.md' - - Testing: 'testing.md' + - Testing: + - Testing: 'testing.md' + - Writing Tests: 'writing_tests.md' - Loading Pulse data: 'pulseload.md' - Code Style: 'code_style.md' - Accessibility: 'accessibility.md'