Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes issue with mail.outbox #1187

Merged
merged 26 commits into from
Apr 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
1fe4ef7
Update plugin.py
kingbuzzman Mar 31, 2025
acd9d61
Update plugin.py
kingbuzzman Mar 31, 2025
502380f
Update test_environment.py
kingbuzzman Mar 31, 2025
c8427b4
i want to see it fail
kingbuzzman Mar 31, 2025
9e786db
Update test_environment.py
kingbuzzman Mar 31, 2025
b9dee6a
This is a much bigger issue.
kingbuzzman Mar 31, 2025
2b5b197
Update test_environment.py
kingbuzzman Apr 1, 2025
60a07b2
Update test_environment.py
kingbuzzman Apr 1, 2025
e831c77
getting closer..
kingbuzzman Apr 1, 2025
e563131
got to the root issue, its a custom django_test_environment
kingbuzzman Apr 1, 2025
43da9ac
Moved test
kingbuzzman Apr 1, 2025
c8c7355
Fixes linter
kingbuzzman Apr 1, 2025
4e26a87
Adds warning
kingbuzzman Apr 1, 2025
5effbf7
Test the warning
kingbuzzman Apr 1, 2025
c4a9a7b
ehh f-it. this is good enough
kingbuzzman Apr 1, 2025
24d5850
Merge branch 'main' into patch-4
kingbuzzman Apr 1, 2025
f640fe7
Merge branch 'main' into patch-4
kingbuzzman Apr 1, 2025
1d7a615
Adds test for when there is no 'settings' at all
kingbuzzman Apr 2, 2025
cf170fa
Slight change to ensure that there really isnt any settings
kingbuzzman Apr 2, 2025
534a7f2
Gigantic simplification
kingbuzzman Apr 2, 2025
b330a26
Removes unused import
kingbuzzman Apr 2, 2025
80303a4
Updates linter
kingbuzzman Apr 2, 2025
3223687
Slight simplification when asserting tests
kingbuzzman Apr 2, 2025
7acaee2
Changes bluetech wanted
kingbuzzman Apr 3, 2025
4975bc1
Update test_fixtures.py
kingbuzzman Apr 3, 2025
295ba8f
Merge branch 'main' into patch-4
kingbuzzman Apr 3, 2025
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
10 changes: 6 additions & 4 deletions pytest_django/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,8 @@ def _dj_autoclear_mailbox() -> None:

from django.core import mail

del mail.outbox[:]
if hasattr(mail, "outbox"):
mail.outbox.clear()


@pytest.fixture()
Expand All @@ -608,12 +609,13 @@ def mailoutbox(
_dj_autoclear_mailbox: None,
) -> list[django.core.mail.EmailMessage] | None:
"""A clean email outbox to which Django-generated emails are sent."""
if not django_settings_is_configured():
return None
skip_if_no_django()

from django.core import mail

return mail.outbox # type: ignore[no-any-return]
if hasattr(mail, "outbox"):
return mail.outbox # type: ignore[no-any-return]
return []


@pytest.fixture()
Expand Down
10 changes: 8 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,13 @@ def _marker_apifun(
extra_settings: str = "",
create_manage_py: bool = False,
project_root: str | None = None,
create_settings: bool = True,
):
return {
"extra_settings": extra_settings,
"create_manage_py": create_manage_py,
"project_root": project_root,
"create_settings": create_settings,
}


Expand Down Expand Up @@ -135,14 +137,18 @@ def django_pytester(

# Copy the test app to make it available in the new test run
shutil.copytree(str(app_source), str(test_app_path))
tpkg_path.joinpath("the_settings.py").write_text(test_settings)
if options["create_settings"]:
tpkg_path.joinpath("the_settings.py").write_text(test_settings)

# For suprocess tests, pytest's `pythonpath` setting doesn't currently
# work, only the envvar does.
pythonpath = os.pathsep.join(filter(None, [str(REPOSITORY_ROOT), os.getenv("PYTHONPATH", "")]))
monkeypatch.setenv("PYTHONPATH", pythonpath)

monkeypatch.setenv("DJANGO_SETTINGS_MODULE", "tpkg.the_settings")
if options["create_settings"]:
monkeypatch.setenv("DJANGO_SETTINGS_MODULE", "tpkg.the_settings")
else:
monkeypatch.delenv("DJANGO_SETTINGS_MODULE", raising=False)

def create_test_module(test_code: str, filename: str = "test_the_test.py") -> Path:
r = tpkg_path.joinpath(filename)
Expand Down
59 changes: 59 additions & 0 deletions tests/test_fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -825,3 +825,62 @@ def mocked_make_msgid(*args, **kwargs):
result = django_pytester.runpytest_subprocess("--tb=short", "-vv", "-s")
result.stdout.fnmatch_lines(["*test_mailbox_inner*", "django_mail_dnsname_mark", "PASSED*"])
assert result.ret == 0


@pytest.mark.django_project(
create_manage_py=True,
extra_settings="""
EMAIL_BACKEND = "django.core.mail.backends.dummy.EmailBackend"
""",
)
def test_mail_auto_fixture_misconfigured(django_pytester: DjangoPytester) -> None:
"""
django_test_environment fixture can be overridden by user, and that would break mailoutbox fixture.

Normally settings.EMAIL_BACKEND is set to "django.core.mail.backends.locmem.EmailBackend" by django,
along with mail.outbox = []. If this function doesn't run for whatever reason, the
mailoutbox fixture will not work properly.
"""
django_pytester.create_test_module(
"""
import pytest

@pytest.fixture(autouse=True, scope="session")
def django_test_environment(request):
yield
""",
filename="conftest.py",
)

django_pytester.create_test_module(
"""
def test_with_fixture(settings, mailoutbox):
assert mailoutbox == []
assert settings.EMAIL_BACKEND == "django.core.mail.backends.dummy.EmailBackend"

def test_without_fixture():
from django.core import mail
assert not hasattr(mail, "outbox")
"""
)
result = django_pytester.runpytest_subprocess()
result.assert_outcomes(passed=2)


@pytest.mark.django_project(create_settings=False)
def test_no_settings(django_pytester: DjangoPytester) -> None:
django_pytester.create_test_module(
"""
def test_skipped_settings(settings):
assert False

def test_skipped_mailoutbox(mailoutbox):
assert False

def test_mail():
from django.core import mail
assert not hasattr(mail, "outbox")
"""
)
result = django_pytester.runpytest_subprocess()
result.assert_outcomes(passed=1, skipped=2)