File: test_python_path.py

package info (click to toggle)
pytest 9.0.2-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 8,308 kB
  • sloc: python: 65,808; makefile: 45
file content (130 lines) | stat: -rw-r--r-- 4,065 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# mypy: allow-untyped-defs
from __future__ import annotations

import sys
from textwrap import dedent

from _pytest.pytester import Pytester
import pytest


@pytest.fixture()
def file_structure(pytester: Pytester) -> None:
    pytester.makepyfile(
        test_foo="""
        from foo import foo

        def test_foo():
            assert foo() == 1
        """
    )

    pytester.makepyfile(
        test_bar="""
        from bar import bar

        def test_bar():
            assert bar() == 2
        """
    )

    foo_py = pytester.mkdir("sub") / "foo.py"
    content = dedent(
        """
        def foo():
            return 1
        """
    )
    foo_py.write_text(content, encoding="utf-8")

    bar_py = pytester.mkdir("sub2") / "bar.py"
    content = dedent(
        """
        def bar():
            return 2
        """
    )
    bar_py.write_text(content, encoding="utf-8")


def test_one_dir(pytester: Pytester, file_structure) -> None:
    pytester.makefile(".ini", pytest="[pytest]\npythonpath=sub\n")
    result = pytester.runpytest("test_foo.py")
    assert result.ret == 0
    result.assert_outcomes(passed=1)


def test_two_dirs(pytester: Pytester, file_structure) -> None:
    pytester.makefile(".ini", pytest="[pytest]\npythonpath=sub sub2\n")
    result = pytester.runpytest("test_foo.py", "test_bar.py")
    assert result.ret == 0
    result.assert_outcomes(passed=2)


def test_local_plugin(pytester: Pytester, file_structure) -> None:
    """`pythonpath` kicks early enough to load plugins via -p (#11118)."""
    localplugin_py = pytester.path / "sub" / "localplugin.py"
    content = dedent(
        """
        def pytest_load_initial_conftests():
            print("local plugin load")

        def pytest_unconfigure():
            print("local plugin unconfig")
        """
    )
    localplugin_py.write_text(content, encoding="utf-8")

    pytester.makeini("[pytest]\npythonpath=sub\n")
    result = pytester.runpytest("-plocalplugin", "-s", "test_foo.py")
    result.stdout.fnmatch_lines(["local plugin load", "local plugin unconfig"])
    assert result.ret == 0
    result.assert_outcomes(passed=1)


def test_module_not_found(pytester: Pytester, file_structure) -> None:
    """Without the pythonpath setting, the module should not be found."""
    pytester.makefile(".ini", pytest="[pytest]\n")
    result = pytester.runpytest("test_foo.py")
    assert result.ret == pytest.ExitCode.INTERRUPTED
    result.assert_outcomes(errors=1)
    expected_error = "E   ModuleNotFoundError: No module named 'foo'"
    result.stdout.fnmatch_lines([expected_error])


def test_no_config_file(pytester: Pytester, file_structure) -> None:
    """If no configuration file, test should error."""
    result = pytester.runpytest("test_foo.py")
    assert result.ret == pytest.ExitCode.INTERRUPTED
    result.assert_outcomes(errors=1)
    expected_error = "E   ModuleNotFoundError: No module named 'foo'"
    result.stdout.fnmatch_lines([expected_error])


def test_clean_up(pytester: Pytester) -> None:
    """Test that the plugin cleans up after itself."""
    # This is tough to test behaviorally because the cleanup really runs last.
    # So the test make several implementation assumptions:
    # - Cleanup is done in pytest_unconfigure().
    # - Not a hook wrapper.
    # So we can add a hook wrapper ourselves to test what it does.
    pytester.makefile(".ini", pytest="[pytest]\npythonpath=I_SHALL_BE_REMOVED\n")
    pytester.makepyfile(test_foo="""def test_foo(): pass""")

    before: list[str] | None = None
    after: list[str] | None = None

    class Plugin:
        @pytest.hookimpl(tryfirst=True)
        def pytest_unconfigure(self) -> None:
            nonlocal before
            before = sys.path.copy()

    result = pytester.runpytest_inprocess(plugins=[Plugin()])
    after = sys.path.copy()
    assert result.ret == 0

    assert before is not None
    assert after is not None
    assert any("I_SHALL_BE_REMOVED" in entry for entry in before)
    assert not any("I_SHALL_BE_REMOVED" in entry for entry in after)