Skip to content

Commit

Permalink
Replace pytest with Django's built-in runner (#18)
Browse files Browse the repository at this point in the history
* Replace `pytest` with Django's built-in runner

* Drop whitenoise public directory

* Correctly collect coverage for parallel test runs

* Note why certain fixtures are created with each test
  • Loading branch information
RealOrangeOne authored Nov 5, 2024
1 parent fed96ed commit 87d5cc1
Show file tree
Hide file tree
Showing 10 changed files with 65 additions and 204 deletions.
1 change: 1 addition & 0 deletions .docker/bashrc.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ alias dj="django-admin"
if [ -n "$DEVCONTAINER" ]
then
alias djrun="django-admin runserver 0.0.0.0:8000"
alias djtest="python manage.py test --settings=cms.settings.test"
alias djrunplus="python manage.py runserver_plus 0.0.0.0:8000"
alias honcho="honcho -f .docker/Procfile"
fi
6 changes: 4 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ all: ## Show the available make targets.

.PHONY: clean
clean: ## Clean the temporary files.
rm -rf .pytest_cache
rm -rf .mypy_cache
rm -rf .coverage
rm -rf .ruff_cache
Expand Down Expand Up @@ -52,7 +51,10 @@ lint-frontend: ## Run front-end linters

.PHONY: test
test: ## Run the tests and check coverage.
poetry run pytest -n auto --ds=cms.settings.test --cov=cms --cov-report term-missing
poetry run coverage erase
poetry run coverage run ./manage.py test --parallel --settings=cms.settings.test
poetry run coverage combine
poetry run coverage report

.PHONY: mypy
mypy: ## Run mypy.
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,13 +168,14 @@ poetry add wagtailmedia

### Run Tests with Coverage

The unit tests are written using the [pytest](https://docs.pytest.org/en/stable/) framework. To run the tests and check
coverage, run:
To run the tests and check coverage, run:

```bash
make test
```

During tests, the `cms.settings.test` settings module is used. When running test without using `make test`, ensure this settings module is used.

### Linting and Formatting

Various tools are used to lint and format the code in this project.
Expand Down
57 changes: 33 additions & 24 deletions cms/core/tests/test_context_processors.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,42 @@
import pytest
from django.test import RequestFactory, TestCase
from wagtail.models import Site

from cms.core.context_processors import global_vars
from cms.core.models import Tracking

pytestmark = pytest.mark.django_db

class ContextProcessorTestCase(TestCase):
"""Tests for context processors."""
def setUp(self):
request_factory = RequestFactory()

def test_when_no_tracking_settings_defined(rf):
"""Check the global vars include sensible defaults when no Tracking settings defined."""
request = rf.get("/")
assert global_vars(request) == {
"GOOGLE_TAG_MANAGER_ID": "",
"SEO_NOINDEX": False,
"LANGUAGE_CODE": "en-gb",
"IS_EXTERNAL_ENV": False,
}
# Request is created with each test to avoid mutation side-effects
self.request = request_factory.get("/")

def test_when_no_tracking_settings_defined(self):
"""Check the global vars include sensible defaults when no Tracking settings defined."""
self.assertEqual(
global_vars(self.request),
{
"GOOGLE_TAG_MANAGER_ID": "",
"SEO_NOINDEX": False,
"LANGUAGE_CODE": "en-gb",
"IS_EXTERNAL_ENV": False,
},
)

def test_when_tracking_settings_defined(rf):
"""Confirm the global vars include Tracking settings when defined."""
Tracking.objects.create(
site=Site.objects.get(is_default_site=True),
google_tag_manager_id="GTM-123456",
)
request = rf.get("/")
assert global_vars(request) == {
"GOOGLE_TAG_MANAGER_ID": "GTM-123456",
"SEO_NOINDEX": False,
"LANGUAGE_CODE": "en-gb",
"IS_EXTERNAL_ENV": False,
}
def test_when_tracking_settings_defined(self):
"""Confirm the global vars include Tracking settings when defined."""
Tracking.objects.create(
site=Site.objects.get(is_default_site=True),
google_tag_manager_id="GTM-123456",
)
self.assertEqual(
global_vars(self.request),
{
"GOOGLE_TAG_MANAGER_ID": "GTM-123456",
"SEO_NOINDEX": False,
"LANGUAGE_CODE": "en-gb",
"IS_EXTERNAL_ENV": False,
},
)
21 changes: 13 additions & 8 deletions cms/core/tests/test_views.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import logging

import pytest
from django.test import Client, TestCase


@pytest.mark.django_db
def test_csrf_token_mismatch_logs_an_error(csrf_check_client, caplog, enable_console_logging): # pylint: disable=unused-argument
"""Check that the custom csrf error view logs CSRF failures."""
csrf_check_client.cookies["csrftoken"] = "wrong"
class CSRFTestCase(TestCase):
"""Tests for CSRF enforcement."""
def setUp(self):
# Client is created with each test to avoid mutation side-effects
self.client = Client(enforce_csrf_checks=True)

with caplog.at_level(logging.ERROR, logger="django.security.csrf"):
csrf_check_client.post("/admin/login/", {})
def test_csrf_token_mismatch_logs_an_error(self):
"""Check that the custom csrf error view logs CSRF failures."""
self.client.cookies["csrftoken"] = "wrong"

assert "CSRF Failure: CSRF cookie" in caplog.text
with self.assertLogs(logger="django.security.csrf", level=logging.ERROR) as logs:
self.client.post("/admin/login/", {})

self.assertIn("CSRF Failure: CSRF cookie", logs.output[0])
3 changes: 0 additions & 3 deletions cms/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,9 +275,6 @@
"staticfiles": {"BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage"},
}

# Place static files that need a specific URL (such as robots.txt and favicon.ico) in the "public" folder
WHITENOISE_ROOT = BASE_DIR / "public"


# This is where Django will look for static files outside the directories of
# applications which are used by default.
Expand Down
13 changes: 7 additions & 6 deletions cms/settings/tests/test_settings.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import pytest
from django.test import TestCase


@pytest.mark.django_db
def test_referrer_policy(client):
"""Test that we have a Referrer-Policy header."""
response = client.get("/")
assert response["Referrer-Policy"] == "no-referrer-when-downgrade"
class ReferrerPolicyTestCase(TestCase):
"""Tests for the Referrer-Policy header."""
def test_referrer_policy(self):
"""Test that we have a Referrer-Policy header."""
response = self.client.get("/")
self.assertEqual(response["Referrer-Policy"], "no-referrer-when-downgrade")
31 changes: 0 additions & 31 deletions conftest.py

This file was deleted.

118 changes: 1 addition & 117 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 3 additions & 11 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,8 @@ pylint = "^3.3.1"
pylint-django = "^2.0.0"
ruff = "^0.7.1" # keep version in sync with .pre-commit-config.yaml

pytest = "^8.3.3"
pytest-cov = "^5.0.0"
pytest-xdist = "^3.6.1"
pytest-django ="^4.9.0"
wagtail-factories = "^4.1.0"
coverage = "^7.6.4"

dslr = "^0.4.0"
django-debug-toolbar = "^4.4.6"
Expand Down Expand Up @@ -136,13 +133,6 @@ ignore_errors = true
[tool.django-stubs]
django_settings_module = "cms.settings.test"

[tool.pytest.ini_options]
DJANGO_SETTINGS_MODULE = "cms.settings.test"
filterwarnings = [
"ignore::django.utils.deprecation.RemovedInNextVersionWarning",
"ignore::DeprecationWarning:l18n.translation"
]

[tool.coverage.run]
source = ["cms"]
omit = [
Expand All @@ -154,6 +144,8 @@ omit = [
"**/migrations/*",
"**/tests/*",
]
parallel = true
concurrency = ["multiprocessing", "thread"]

[tool.coverage.report]
show_missing = true
Expand Down

0 comments on commit 87d5cc1

Please sign in to comment.