Skip to content

Commit 05144fb

Browse files
committed
Run test database through Docker
This PR makes pytest control Docker, rather than rely on it being manually started. This makes it easier to run tests from scratch, guarantees separate database servers for parallel test runs, and allows us to vary the database more easily too.
1 parent 189d11f commit 05144fb

5 files changed

Lines changed: 89 additions & 55 deletions

File tree

.github/workflows/main.yml

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,8 @@ jobs:
3535
- mariadb:10.11
3636
- mariadb:10.6
3737

38-
services:
39-
database:
40-
image: ${{ matrix.database }}
41-
env:
42-
MYSQL_ROOT_PASSWORD: hunter2
43-
ports:
44-
- 3306:3306
45-
options: --tmpfs /var/lib/mysql
46-
4738
env:
48-
DB_HOST: 127.0.0.1
49-
DB_USER: root
50-
DB_PASSWORD: hunter2
39+
DB_IMAGE: ${{ matrix.database }}
5140

5241
steps:
5342
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

CONTRIBUTING.rst

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,35 +5,27 @@ Contributing
55
Run the tests
66
-------------
77

8-
1. Install `tox <https://tox.wiki/en/latest/>`__.
8+
1. Install `tox <https://tox.wiki/en/latest/>`__ and ensure Docker is running.
99

10-
2. Run a supported version of MySQL or MariaDB.
11-
This is easiest with the official Docker images.
12-
For example:
10+
2. Run the tests:
1311

1412
.. code-block:: console
1513
16-
docker run --detach --name mariadb -e MYSQL_ROOT_PASSWORD=hunter2 --publish 3306:3306 mariadb:11.6
14+
tox -e py314-django61
1715
18-
3. Run the tests by passing environment variables with your connection parameters.
19-
For the above Docker command:
16+
By default this uses the ``mariadb:11.4`` image.
17+
To use a different image, set ``DB_IMAGE``:
2018

2119
.. code-block:: console
2220
23-
DB_HOST=127.0.0.1 DB_USER=root DB_PASSWORD='hunter2' tox -e py313-django52
21+
DB_IMAGE=mysql:8.4 tox -e py314-django61
2422
25-
tox environments are split per Python and Django version.
23+
tox environments are split per Python and Django version.
2624

27-
You can run a subset of tests by passing them after ``--`` like:
28-
29-
.. code-block:: console
30-
31-
DB_HOST=127.0.0.1 DB_USER=root DB_PASSWORD='hunter2' tox -e py313-django51 -- tests/testapp/test_cache.py
32-
33-
You can also pass other pytest arguments after the ``--``.
34-
35-
4. When you’re done, shut down the Docker container with:
25+
You can run a subset of tests by passing them after ``--`` like:
3626

3727
.. code-block:: console
3828
39-
docker rm --force mariadb
29+
tox -e py314-django61 -- tests/testapp/test_cache.py
30+
31+
You can also pass other pytest arguments after the ``--``.

tests/conftest.py

Lines changed: 66 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,77 @@
11
from __future__ import annotations
22

3+
import os
4+
import subprocess
5+
import time
36
import warnings
47

5-
import django
6-
from django.db import connection
7-
from pytest_django.plugin import blocking_manager_key
8+
import MySQLdb
9+
import pytest
10+
from django.conf import settings
811

912

10-
def pytest_report_header(config):
11-
dot_version = ".".join(str(x) for x in django.VERSION)
12-
header = "Django version: " + dot_version
13+
@pytest.fixture(scope="session", autouse=True)
14+
def mysql_server():
15+
image = os.environ.get("DB_IMAGE", "mariadb:11.4")
16+
name = f"django-mysql-test-{os.getpid()}"
1317

14-
pytest_django_db_blocker = config.stash[blocking_manager_key]
15-
with pytest_django_db_blocker.unblock(), connection._nodb_cursor() as cursor:
16-
cursor.execute("SELECT VERSION()")
17-
version = cursor.fetchone()[0]
18-
header += f"\nMySQL version: {version}"
18+
subprocess.run(
19+
[
20+
"docker",
21+
"run",
22+
"--detach",
23+
"--name",
24+
name,
25+
"-e",
26+
"MYSQL_ROOT_PASSWORD=hunter2",
27+
"--publish",
28+
"127.0.0.1::3306",
29+
image,
30+
],
31+
check=True,
32+
)
1933

20-
return header
34+
try:
35+
port = (
36+
subprocess.check_output(
37+
[
38+
"docker",
39+
"inspect",
40+
name,
41+
"--format",
42+
'{{(index (index .NetworkSettings.Ports "3306/tcp") 0).HostPort}}',
43+
]
44+
)
45+
.decode()
46+
.strip()
47+
)
48+
49+
for db in settings.DATABASES.values():
50+
if db["ENGINE"] != "django.db.backends.sqlite3":
51+
db["HOST"] = "127.0.0.1"
52+
db["PORT"] = port
53+
db["USER"] = "root"
54+
db["PASSWORD"] = "hunter2"
55+
56+
deadline = time.monotonic() + 60
57+
while True:
58+
try:
59+
conn = MySQLdb.connect(
60+
host="127.0.0.1",
61+
port=int(port),
62+
user="root",
63+
password="hunter2",
64+
)
65+
conn.close()
66+
break
67+
except MySQLdb.OperationalError:
68+
if time.monotonic() > deadline:
69+
raise RuntimeError("MySQL did not become ready in time")
70+
time.sleep(0.5)
71+
72+
yield
73+
finally:
74+
subprocess.run(["docker", "rm", "--force", name], check=True)
2175

2276

2377
# MySQL 5.7 warns about some sql mode changes

tests/settings.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,22 @@
1212
"default": {
1313
"ENGINE": "tests.db_backend",
1414
"NAME": "django_mysql",
15-
"USER": os.environ.get("DB_USER", ""),
16-
"PASSWORD": os.environ.get("DB_PASSWORD", ""),
17-
"HOST": os.environ.get("DB_HOST", ""),
18-
"PORT": os.environ.get("DB_PORT", ""),
15+
# Filled in by conftest.py
16+
"USER": "",
17+
"PASSWORD": "",
18+
"HOST": "",
19+
"PORT": "",
1920
"OPTIONS": {"charset": "utf8mb4"},
2021
"TEST": {"COLLATION": "utf8mb4_general_ci", "CHARSET": "utf8mb4"},
2122
},
2223
"other": {
2324
"ENGINE": "tests.db_backend",
2425
"NAME": "django_mysql2",
25-
"USER": os.environ.get("DB_USER", ""),
26-
"PASSWORD": os.environ.get("DB_PASSWORD", ""),
27-
"HOST": os.environ.get("DB_HOST", ""),
28-
"PORT": os.environ.get("DB_PORT", ""),
26+
# Filled in by conftest.py
27+
"USER": "",
28+
"PASSWORD": "",
29+
"HOST": "",
30+
"PORT": "",
2931
"OPTIONS": {"charset": "utf8mb4"},
3032
"TEST": {"COLLATION": "utf8mb4_general_ci", "CHARSET": "utf8mb4"},
3133
},

tox.ini

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,7 @@ runner = uv-venv-lock-runner
1313
package = wheel
1414
wheel_build_env = .pkg
1515
pass_env =
16-
DB_HOST
17-
DB_PASSWORD
18-
DB_PORT
19-
DB_USER
16+
DB_IMAGE
2017
set_env =
2118
PYTHONDEVMODE = 1
2219
commands =

0 commit comments

Comments
 (0)