Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,15 @@ jobs:
run: |
coverage run -m pytest

- name: Test Example Project
env:
DJANGO_VERSION: ${{ matrix.django }}
working-directory: example/
run: |
uv pip install "django~=${DJANGO_VERSION}"
uv sync --locked --inexact --group dev
coverage run --append -m pytest

# Converts the binary `.coverage` file to a XML file
# that codecov can understand
- name: Export Coverage
Expand Down
11 changes: 8 additions & 3 deletions example/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
[build-system]
requires = ["uv_build>=0.10.7,<0.11.0"]
build-backend = "uv_build"

[project]
name = "example"
version = "0.1.0"
description = "Add your description here"
description = "Example Django project that uses pytest-django-queries"
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"django",
"django>=4.2.0",
]
package = true

[dependency-groups]
dev = [
"pytest>=9.0.2",
"pytest>=7.2.0",
"pytest-django>=4.12.0",
"pytest-django-queries",
]
Expand Down
12 changes: 11 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ dev = [
]
test = [
"coverage",
"pytest-django",
"pytest-xdist",
]

Expand Down Expand Up @@ -143,7 +144,16 @@ members = [
".",
]

[tool.pytest.ini_options]
[tool.pytest]
addopts = [
# Prevents autoloading plugins as they could impact the test
# results, e.g., we could fail to detect that the plugin
# doesn't work if pytest-django isn't installed (as it's an
# optional plugin)
"--disable-plugin-autoload",
"-p", "xdist",
"-p", "django_queries",
]
testpaths = [
"tests",
]
36 changes: 25 additions & 11 deletions src/pytest_django_queries/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
import pytest
from django.test.utils import CaptureQueriesContext

# Defines the plugin marker name
from pytest_django_queries.utils import create_backup

# Defines the plugin marker name
PYTEST_QUERY_COUNT_MARKER = "count_queries"
PYTEST_QUERY_COUNT_FIXTURE_NAME = "count_queries"
DEFAULT_RESULT_FILENAME = ".pytest-queries"
Expand All @@ -29,11 +29,11 @@ def get_worker_input(node):
return workerinput


def is_worker(config):
def is_worker(config: pytest.Config):
return hasattr(config, "workerinput") or hasattr(config, "slaveinput")


def get_workerid(config):
def get_workerid(config: pytest.Config):
if hasattr(config, "workerinput"):
return config.workerinput["workerid"]
elif hasattr(config, "slaveinput"):
Expand All @@ -51,7 +51,7 @@ def save_results_to_json(save_path, backup_path, data):
json.dump(data, fp, indent=2)


def add_entry(request, queries, dirout):
def add_entry(request: pytest.FixtureRequest, queries, dirout):
module_name = request.node.module.__name__
test_name = request.node.name
queries = queries[:]
Expand Down Expand Up @@ -119,14 +119,19 @@ def pytest_load_initial_conftests(early_config, parser, args):
early_config.known_args_namespace.queries_backup_results = backup_path


def _process_query_count_marker(request, *_args, **kwargs):
def _process_query_count_marker(
config: pytest.Config, request: pytest.FixtureRequest, *_args, **kwargs
):
autouse = kwargs.setdefault("autouse", True)
if autouse:
# Force load
if config.dj_queries_has_django_plugin is True:
request.getfixturevalue("_django_db_marker")
request.getfixturevalue(PYTEST_QUERY_COUNT_FIXTURE_NAME)


@pytest.fixture(autouse=True)
def _pytest_query_marker(request):
def _pytest_query_marker(request: pytest.FixtureRequest):
"""Use the fixture to count the queries on the current node if it's
marked with 'count_queries'.

Expand All @@ -138,16 +143,25 @@ def _pytest_query_marker(request):
but place the fixture manually."""
marker = request.node.get_closest_marker(PYTEST_QUERY_COUNT_MARKER)
if marker:
_process_query_count_marker(request, *marker.args, **marker.kwargs)
_process_query_count_marker(
request.config, request, *marker.args, **marker.kwargs
)


def pytest_configure(config):
def pytest_configure(config: pytest.Config) -> None:
config.django_queries_shared_directory = tempfile.mkdtemp(
prefix="pytest-django-queries"
)

# Stores whether the pytest-django plugin is installed
# Note: both names are valid, 'django' is an alias for 'pytest_django.plugin'
# (https://github.com/pytest-dev/pytest-django/blob/3955b1826f396cecd01d1c059e7703769ad94d81/pyproject.toml#L77-L78)
config.dj_queries_has_django_plugin = config.pluginmanager.hasplugin(
"django"
) or config.pluginmanager.hasplugin("pytest_django.plugin")


def pytest_unconfigure(config):
def pytest_unconfigure(config: pytest.Config):
results_path = config.django_queries_shared_directory
test_results = {}

Expand Down Expand Up @@ -188,7 +202,7 @@ def pytest_configure_node(node):
)


def get_shared_directory(request):
def get_shared_directory(request: pytest.FixtureRequest):
"""Returns a unique and temporary directory which can be shared by
master or worker nodes in xdist runs.
"""
Expand All @@ -199,7 +213,7 @@ def get_shared_directory(request):


@pytest.fixture
def count_queries(request):
def count_queries(request: pytest.FixtureRequest):
"""Wrap a test to count the number of performed queries."""
from django.db import connection

Expand Down
11 changes: 11 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@

pytest_plugins = "pytester"

# Prevents autoloading plugins as they could impact the test results,
# e.g., we could fail to detect that the plugin doesn't work if
# pytest-django isn't installed (as it's an optional plugin)
DEFAULT_PYTEST_FLAGS = [
"--disable-plugin-autoload",
"-p",
"xdist",
"-p",
"django_queries",
]


@pytest.fixture
def valid_comparison_entries():
Expand Down
55 changes: 42 additions & 13 deletions tests/test_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import mock
import pytest

from tests.conftest import DEFAULT_PYTEST_FLAGS

DUMMY_TEST_QUERY = """
import pytest

Expand All @@ -32,7 +34,9 @@ def test_fixture_is_invoked_when_marked(testdir):
# Run a dummy test that performs queries
# and triggers a counting of the query number
testdir.makepyfile(test_file=DUMMY_TEST_QUERY)
results = testdir.runpytest("--django-db-bench", results_path)
results = testdir.runpytest(
*DEFAULT_PYTEST_FLAGS, "--django-db-bench", results_path
)

# Ensure the tests have passed
results.assert_outcomes(1, 0, 0)
Expand All @@ -56,7 +60,9 @@ def test_plugin_exports_nothing_if_empty(testdir):
def test_nothing():
pass
""")
results = testdir.runpytest("--django-db-bench", results_path)
results = testdir.runpytest(
*DEFAULT_PYTEST_FLAGS, "--django-db-bench", results_path
)

# Ensure the tests have passed
results.assert_outcomes(1, 0, 0)
Expand All @@ -80,7 +86,9 @@ def test_plugin_exports_results_even_when_test_fails(testdir):
def test_failure():
assert 0
""")
results = testdir.runpytest("--django-db-bench", results_path)
results = testdir.runpytest(
*DEFAULT_PYTEST_FLAGS, "--django-db-bench", results_path
)

# Ensure the tests have failed
results.assert_outcomes(0, 0, 1)
Expand Down Expand Up @@ -114,7 +122,9 @@ def fixture_with_db_queries():
def test_with_side_effects(fixture_with_db_queries, count_queries):
pass
""")
results = testdir.runpytest("--django-db-bench", results_path)
results = testdir.runpytest(
*DEFAULT_PYTEST_FLAGS, "--django-db-bench", results_path
)

# Ensure the tests have passed
results.assert_outcomes(1, 0, 0)
Expand All @@ -140,7 +150,9 @@ def test_plugin_marker_without_autouse_disabled(testdir):
def test_without_autouse():
pass
""")
results = testdir.runpytest("--django-db-bench", results_path)
results = testdir.runpytest(
*DEFAULT_PYTEST_FLAGS, "--django-db-bench", results_path
)

# Ensure the tests have passed
results.assert_outcomes(1, 0, 0)
Expand All @@ -159,7 +171,11 @@ def test_fixture_is_backing_up_old_results(testdir):
testdir.makepyfile(test_file=DUMMY_TEST_QUERY)

results = testdir.runpytest(
"--django-db-bench", results_path, "--django-backup-queries", old_results_path
*DEFAULT_PYTEST_FLAGS,
"--django-db-bench",
results_path,
"--django-backup-queries",
old_results_path,
)

# Ensure the tests have passed
Expand All @@ -177,7 +193,11 @@ def test_fixture_is_backing_up_old_results(testdir):

# Run again the tests
results = testdir.runpytest(
"--django-db-bench", results_path, "--django-backup-queries", old_results_path
*DEFAULT_PYTEST_FLAGS,
"--django-db-bench",
results_path,
"--django-backup-queries",
old_results_path,
)

# Ensure the tests have passed
Expand Down Expand Up @@ -211,7 +231,9 @@ def test_fixture_is_not_backing_up_if_not_asked_to(testdir):
testdir.makepyfile(test_file=DUMMY_TEST_QUERY)

with mock.patch("pytest_django_queries.plugin.create_backup") as mocked_backup:
results = testdir.runpytest("--django-db-bench", results_path)
results = testdir.runpytest(
*DEFAULT_PYTEST_FLAGS, "--django-db-bench", results_path
)
assert mocked_backup.call_count == 0

# Ensure the tests have passed
Expand All @@ -232,7 +254,10 @@ def test_fixture_is_backing_up_old_results_to_default_path_if_no_path_provided(t
from pytest_django_queries.plugin import DEFAULT_OLD_RESULT_FILENAME

results = testdir.runpytest(
"--django-db-bench", results_path, "--django-backup-queries"
*DEFAULT_PYTEST_FLAGS,
"--django-db-bench",
results_path,
"--django-backup-queries",
)
mocked_backup.assert_called_with(str(results_path), DEFAULT_OLD_RESULT_FILENAME)

Expand All @@ -243,7 +268,7 @@ def test_fixture_is_backing_up_old_results_to_default_path_if_no_path_provided(t

def test_marker_message(testdir):
"""Ensure the custom markers configuration is added to pytest."""
result = testdir.runpytest("--markers")
result = testdir.runpytest(*DEFAULT_PYTEST_FLAGS, "--markers")
result.stdout.fnmatch_lines(
[
"@pytest.mark.count_queries: "
Expand All @@ -254,7 +279,7 @@ def test_marker_message(testdir):

def test_implements_custom_options(testdir):
"""Ensure the custom options are added to pytest."""
result = testdir.runpytest("--help")
result = testdir.runpytest(*DEFAULT_PYTEST_FLAGS, "--help")
result.stdout.fnmatch_lines(
[
"django-queries:",
Expand Down Expand Up @@ -286,7 +311,9 @@ def test_foo():
cursor.execute("SELECT 1;")
cursor.fetchone()""")

results = testdir.runpytest("--django-db-bench", results_path, script)
results = testdir.runpytest(
*DEFAULT_PYTEST_FLAGS, "--django-db-bench", results_path, script
)

# Ensure the tests have passed
results.assert_outcomes(1, 0, 0)
Expand Down Expand Up @@ -324,7 +351,9 @@ def test_foo(foo):
# Append current test files into the temporary test directory in order
# to have settings.py available for PyPi packages
shutil.copytree(os.path.dirname(__file__), os.path.join(str(testdir) + "/tests"))
results = testdir.runpytest("--django-db-bench", results_path, "-n", "5", script)
results = testdir.runpytest(
*DEFAULT_PYTEST_FLAGS, "--django-db-bench", results_path, "-n", "5", script
)

# Ensure the tests have passed
results.assert_outcomes(500, 0, 0)
Expand Down
Loading