From 8b956e4e28781a38fd8bb7b21f7d44b7cddb965f Mon Sep 17 00:00:00 2001 From: Mathieu Kniewallner Date: Sat, 6 May 2023 11:53:50 +0200 Subject: [PATCH] test: split unit/functional tests and reduce project bootstrap boilerplate (#350) * test: split unit and functional tests * test: reduce project bootstrap boilerplate --- pyproject.toml | 2 +- tests/cli/test_cli_pdm.py | 39 --------- tests/cli/test_cli_pep_621.py | 39 --------- .../test_cli_pyproject_different_directory.py | 39 --------- tests/cli/test_cli_src_directory.py | 39 --------- tests/{cli => functional}/__init__.py | 0 .../cli}/__init__.py | 0 tests/{ => functional}/cli/test_cli.py | 57 ++++++------- .../cli/test_cli_arg_types.py | 0 .../cli/test_cli_gitignore.py | 24 ++---- tests/functional/cli/test_cli_pdm.py | 24 ++++++ tests/functional/cli/test_cli_pep_621.py | 24 ++++++ .../test_cli_pyproject_different_directory.py | 24 ++++++ .../cli/test_cli_requirements_txt.py | 33 ++------ .../functional/cli/test_cli_src_directory.py | 24 ++++++ tests/functional/conftest.py | 79 +++++++++++++++++++ tests/functional/types.py | 16 ++++ tests/{imports => unit}/__init__.py | 0 .../dependency_getter}/__init__.py | 0 .../{ => unit}/dependency_getter/test_pdm.py | 0 .../dependency_getter/test_pep_621.py | 0 .../dependency_getter/test_poetry.py | 0 .../test_requirements_txt.py | 0 tests/unit/imports/__init__.py | 0 tests/{ => unit}/imports/test_extract.py | 0 tests/unit/issues_finder/__init__.py | 0 .../issues_finder/test_misplaced_dev.py | 0 .../{ => unit}/issues_finder/test_missing.py | 0 .../{ => unit}/issues_finder/test_obsolete.py | 0 .../issues_finder/test_transitive.py | 0 tests/{ => unit}/test_config.py | 0 tests/{ => unit}/test_core.py | 0 tests/{ => unit}/test_dependency.py | 0 .../test_dependency_specification_detector.py | 0 tests/{ => unit}/test_json_writer.py | 0 tests/{ => unit}/test_module.py | 0 tests/{ => unit}/test_python_file_finder.py | 0 tests/{ => unit}/test_result_logger.py | 0 tests/{ => unit}/test_utils.py | 0 39 files changed, 225 insertions(+), 238 deletions(-) delete mode 100644 tests/cli/test_cli_pdm.py delete mode 100644 tests/cli/test_cli_pep_621.py delete mode 100644 tests/cli/test_cli_pyproject_different_directory.py delete mode 100644 tests/cli/test_cli_src_directory.py rename tests/{cli => functional}/__init__.py (100%) rename tests/{dependency_getter => functional/cli}/__init__.py (100%) rename tests/{ => functional}/cli/test_cli.py (72%) rename tests/{ => functional}/cli/test_cli_arg_types.py (100%) rename tests/{ => functional}/cli/test_cli_gitignore.py (53%) create mode 100644 tests/functional/cli/test_cli_pdm.py create mode 100644 tests/functional/cli/test_cli_pep_621.py create mode 100644 tests/functional/cli/test_cli_pyproject_different_directory.py rename tests/{ => functional}/cli/test_cli_requirements_txt.py (50%) create mode 100644 tests/functional/cli/test_cli_src_directory.py create mode 100644 tests/functional/conftest.py create mode 100644 tests/functional/types.py rename tests/{imports => unit}/__init__.py (100%) rename tests/{issues_finder => unit/dependency_getter}/__init__.py (100%) rename tests/{ => unit}/dependency_getter/test_pdm.py (100%) rename tests/{ => unit}/dependency_getter/test_pep_621.py (100%) rename tests/{ => unit}/dependency_getter/test_poetry.py (100%) rename tests/{ => unit}/dependency_getter/test_requirements_txt.py (100%) create mode 100644 tests/unit/imports/__init__.py rename tests/{ => unit}/imports/test_extract.py (100%) create mode 100644 tests/unit/issues_finder/__init__.py rename tests/{ => unit}/issues_finder/test_misplaced_dev.py (100%) rename tests/{ => unit}/issues_finder/test_missing.py (100%) rename tests/{ => unit}/issues_finder/test_obsolete.py (100%) rename tests/{ => unit}/issues_finder/test_transitive.py (100%) rename tests/{ => unit}/test_config.py (100%) rename tests/{ => unit}/test_core.py (100%) rename tests/{ => unit}/test_dependency.py (100%) rename tests/{ => unit}/test_dependency_specification_detector.py (100%) rename tests/{ => unit}/test_json_writer.py (100%) rename tests/{ => unit}/test_module.py (100%) rename tests/{ => unit}/test_python_file_finder.py (100%) rename tests/{ => unit}/test_result_logger.py (100%) rename tests/{ => unit}/test_utils.py (100%) diff --git a/pyproject.toml b/pyproject.toml index a676b21b..75ceb124 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -96,7 +96,7 @@ show_error_codes = true module = [ "deptry.core", "scripts.generate_stdlibs", - "tests.test_core", + "tests.unit.test_core", ] warn_unused_ignores = false diff --git a/tests/cli/test_cli_pdm.py b/tests/cli/test_cli_pdm.py deleted file mode 100644 index d81a2ba5..00000000 --- a/tests/cli/test_cli_pdm.py +++ /dev/null @@ -1,39 +0,0 @@ -from __future__ import annotations - -import shlex -import shutil -import subprocess -from typing import TYPE_CHECKING - -import pytest -from click.testing import CliRunner - -from deptry.cli import deptry -from tests.utils import get_issues_report, run_within_dir - -if TYPE_CHECKING: - from pathlib import Path - - from _pytest.tmpdir import TempPathFactory - - -@pytest.fixture(scope="session") -def pdm_dir_with_venv_installed(tmp_path_factory: TempPathFactory) -> Path: - tmp_path_proj = tmp_path_factory.getbasetemp() / "project_with_pdm" - shutil.copytree("tests/data/project_with_pdm", str(tmp_path_proj)) - with run_within_dir(tmp_path_proj): - assert subprocess.check_call(shlex.split("pip install pdm; pdm install")) == 0 - return tmp_path_proj - - -def test_cli_with_pdm(pdm_dir_with_venv_installed: Path) -> None: - with run_within_dir(pdm_dir_with_venv_installed): - result = CliRunner().invoke(deptry, ". -o report.json") - - assert result.exit_code == 1 - assert get_issues_report() == { - "misplaced_dev": ["black"], - "missing": ["white"], - "obsolete": ["isort", "requests"], - "transitive": [], - } diff --git a/tests/cli/test_cli_pep_621.py b/tests/cli/test_cli_pep_621.py deleted file mode 100644 index 69753060..00000000 --- a/tests/cli/test_cli_pep_621.py +++ /dev/null @@ -1,39 +0,0 @@ -from __future__ import annotations - -import shlex -import shutil -import subprocess -from typing import TYPE_CHECKING - -import pytest -from click.testing import CliRunner - -from deptry.cli import deptry -from tests.utils import get_issues_report, run_within_dir - -if TYPE_CHECKING: - from pathlib import Path - - from _pytest.tmpdir import TempPathFactory - - -@pytest.fixture(scope="session") -def pep_621_dir_with_venv_installed(tmp_path_factory: TempPathFactory) -> Path: - tmp_path_proj = tmp_path_factory.getbasetemp() / "pep_621_project" - shutil.copytree("tests/data/pep_621_project", str(tmp_path_proj)) - with run_within_dir(tmp_path_proj): - assert subprocess.check_call(shlex.split("pip install .")) == 0 - return tmp_path_proj - - -def test_cli_with_pep_621(pep_621_dir_with_venv_installed: Path) -> None: - with run_within_dir(pep_621_dir_with_venv_installed): - result = CliRunner().invoke(deptry, ". -o report.json") - - assert result.exit_code == 1 - assert get_issues_report() == { - "misplaced_dev": [], - "missing": ["white"], - "obsolete": ["isort", "requests", "mypy", "pytest"], - "transitive": [], - } diff --git a/tests/cli/test_cli_pyproject_different_directory.py b/tests/cli/test_cli_pyproject_different_directory.py deleted file mode 100644 index 3b9d9c3b..00000000 --- a/tests/cli/test_cli_pyproject_different_directory.py +++ /dev/null @@ -1,39 +0,0 @@ -from __future__ import annotations - -import shlex -import shutil -import subprocess -from typing import TYPE_CHECKING - -import pytest -from click.testing import CliRunner - -from deptry.cli import deptry -from tests.utils import get_issues_report, run_within_dir - -if TYPE_CHECKING: - from pathlib import Path - - from _pytest.tmpdir import TempPathFactory - - -@pytest.fixture(scope="session") -def pep_621_dir_with_pyproject_different_directory(tmp_path_factory: TempPathFactory) -> Path: - tmp_path_proj = tmp_path_factory.getbasetemp() / "project_with_pyproject_different_directory" - shutil.copytree("tests/data/project_with_pyproject_different_directory", str(tmp_path_proj)) - with run_within_dir(tmp_path_proj): - assert subprocess.check_call(shlex.split("pip install ."), cwd="a_sub_directory") == 0 - return tmp_path_proj - - -def test_cli_with_pyproject_different_directory(pep_621_dir_with_pyproject_different_directory: Path) -> None: - with run_within_dir(pep_621_dir_with_pyproject_different_directory): - result = CliRunner().invoke(deptry, "src --config a_sub_directory/pyproject.toml -o report.json") - - assert result.exit_code == 1 - assert get_issues_report() == { - "misplaced_dev": [], - "missing": ["white"], - "obsolete": ["isort", "requests", "mypy", "pytest"], - "transitive": [], - } diff --git a/tests/cli/test_cli_src_directory.py b/tests/cli/test_cli_src_directory.py deleted file mode 100644 index 7d0342ae..00000000 --- a/tests/cli/test_cli_src_directory.py +++ /dev/null @@ -1,39 +0,0 @@ -from __future__ import annotations - -import shlex -import shutil -import subprocess -from typing import TYPE_CHECKING - -import pytest -from click.testing import CliRunner - -from deptry.cli import deptry -from tests.utils import get_issues_report, run_within_dir - -if TYPE_CHECKING: - from pathlib import Path - - from _pytest.tmpdir import TempPathFactory - - -@pytest.fixture(scope="session") -def pep_621_dir_with_src_directory(tmp_path_factory: TempPathFactory) -> Path: - tmp_path_proj = tmp_path_factory.getbasetemp() / "project_with_src_directory" - shutil.copytree("tests/data/project_with_src_directory", str(tmp_path_proj)) - with run_within_dir(tmp_path_proj): - assert subprocess.check_call(shlex.split("pip install .")) == 0 - return tmp_path_proj - - -def test_cli_with_src_directory(pep_621_dir_with_src_directory: Path) -> None: - with run_within_dir(pep_621_dir_with_src_directory): - result = CliRunner().invoke(deptry, "src -o report.json") - - assert result.exit_code == 1 - assert get_issues_report() == { - "misplaced_dev": [], - "missing": ["httpx", "white"], - "obsolete": ["isort", "requests", "mypy", "pytest"], - "transitive": [], - } diff --git a/tests/cli/__init__.py b/tests/functional/__init__.py similarity index 100% rename from tests/cli/__init__.py rename to tests/functional/__init__.py diff --git a/tests/dependency_getter/__init__.py b/tests/functional/cli/__init__.py similarity index 100% rename from tests/dependency_getter/__init__.py rename to tests/functional/cli/__init__.py diff --git a/tests/cli/test_cli.py b/tests/functional/cli/test_cli.py similarity index 72% rename from tests/cli/test_cli.py rename to tests/functional/cli/test_cli.py index 48f1aa13..54807dd1 100644 --- a/tests/cli/test_cli.py +++ b/tests/functional/cli/test_cli.py @@ -2,32 +2,21 @@ import os import shlex -import shutil import subprocess from pathlib import Path from typing import TYPE_CHECKING -import pytest from click.testing import CliRunner from deptry.cli import deptry from tests.utils import get_issues_report, run_within_dir if TYPE_CHECKING: - from _pytest.tmpdir import TempPathFactory + from tests.functional.types import ToolSpecificProjectBuilder -@pytest.fixture(scope="session") -def dir_with_venv_installed(tmp_path_factory: TempPathFactory) -> Path: - tmp_path_proj = tmp_path_factory.getbasetemp() / "example_project" - shutil.copytree("tests/data/example_project", tmp_path_proj) - with run_within_dir(tmp_path_proj): - assert subprocess.check_call(shlex.split("poetry install --no-interaction --no-root")) == 0 - return tmp_path_proj - - -def test_cli_returns_error(dir_with_venv_installed: Path) -> None: - with run_within_dir(dir_with_venv_installed): +def test_cli_returns_error(poetry_project_builder: ToolSpecificProjectBuilder) -> None: + with run_within_dir(poetry_project_builder("example_project")): result = CliRunner().invoke(deptry, ". -o report.json") assert result.exit_code == 1 @@ -39,8 +28,8 @@ def test_cli_returns_error(dir_with_venv_installed: Path) -> None: } -def test_cli_ignore_notebooks(dir_with_venv_installed: Path) -> None: - with run_within_dir(dir_with_venv_installed): +def test_cli_ignore_notebooks(project_builder: ToolSpecificProjectBuilder) -> None: + with run_within_dir(project_builder("example_project", "poetry install --no-interaction --no-root")): result = CliRunner().invoke(deptry, ". --ignore-notebooks -o report.json") assert result.exit_code == 1 @@ -52,22 +41,22 @@ def test_cli_ignore_notebooks(dir_with_venv_installed: Path) -> None: } -def test_cli_ignore_flags(dir_with_venv_installed: Path) -> None: - with run_within_dir(dir_with_venv_installed): +def test_cli_ignore_flags(project_builder: ToolSpecificProjectBuilder) -> None: + with run_within_dir(project_builder("example_project", "poetry install --no-interaction --no-root")): result = CliRunner().invoke(deptry, ". --ignore-obsolete isort,pkginfo,requests -im white -id black") assert result.exit_code == 0 -def test_cli_skip_flags(dir_with_venv_installed: Path) -> None: - with run_within_dir(dir_with_venv_installed): +def test_cli_skip_flags(project_builder: ToolSpecificProjectBuilder) -> None: + with run_within_dir(project_builder("example_project", "poetry install --no-interaction --no-root")): result = CliRunner().invoke(deptry, ". --skip-obsolete --skip-missing --skip-misplaced-dev --skip-transitive") assert result.exit_code == 0 -def test_cli_exclude(dir_with_venv_installed: Path) -> None: - with run_within_dir(dir_with_venv_installed): +def test_cli_exclude(project_builder: ToolSpecificProjectBuilder) -> None: + with run_within_dir(project_builder("example_project", "poetry install --no-interaction --no-root")): result = CliRunner().invoke(deptry, ". --exclude src/notebook.ipynb -o report.json") assert result.exit_code == 1 @@ -79,8 +68,8 @@ def test_cli_exclude(dir_with_venv_installed: Path) -> None: } -def test_cli_extend_exclude(dir_with_venv_installed: Path) -> None: - with run_within_dir(dir_with_venv_installed): +def test_cli_extend_exclude(project_builder: ToolSpecificProjectBuilder) -> None: + with run_within_dir(project_builder("example_project", "poetry install --no-interaction --no-root")): result = CliRunner().invoke(deptry, ". -ee src/notebook.ipynb -o report.json") assert result.exit_code == 1 @@ -92,8 +81,8 @@ def test_cli_extend_exclude(dir_with_venv_installed: Path) -> None: } -def test_cli_known_first_party(dir_with_venv_installed: Path) -> None: - with run_within_dir(dir_with_venv_installed): +def test_cli_known_first_party(project_builder: ToolSpecificProjectBuilder) -> None: + with run_within_dir(project_builder("example_project", "poetry install --no-interaction --no-root")): result = CliRunner().invoke(deptry, ". --known-first-party white -o report.json") assert result.exit_code == 1 @@ -105,8 +94,8 @@ def test_cli_known_first_party(dir_with_venv_installed: Path) -> None: } -def test_cli_not_verbose(dir_with_venv_installed: Path) -> None: - with run_within_dir(dir_with_venv_installed): +def test_cli_not_verbose(project_builder: ToolSpecificProjectBuilder) -> None: + with run_within_dir(project_builder("example_project", "poetry install --no-interaction --no-root")): result = subprocess.run(shlex.split("poetry run deptry . -o report.json"), capture_output=True, text=True) assert result.returncode == 1 @@ -119,8 +108,8 @@ def test_cli_not_verbose(dir_with_venv_installed: Path) -> None: } -def test_cli_verbose(dir_with_venv_installed: Path) -> None: - with run_within_dir(dir_with_venv_installed): +def test_cli_verbose(project_builder: ToolSpecificProjectBuilder) -> None: + with run_within_dir(project_builder("example_project", "poetry install --no-interaction --no-root")): result = subprocess.run( shlex.split("poetry run deptry . --verbose -o report.json"), capture_output=True, text=True ) @@ -135,8 +124,8 @@ def test_cli_verbose(dir_with_venv_installed: Path) -> None: } -def test_cli_with_not_json_output(dir_with_venv_installed: Path) -> None: - with run_within_dir(dir_with_venv_installed): +def test_cli_with_not_json_output(project_builder: ToolSpecificProjectBuilder) -> None: + with run_within_dir(project_builder("example_project", "poetry install --no-interaction --no-root")): # Remove previously generated `report.json`. os.remove("report.json") @@ -155,8 +144,8 @@ def test_cli_with_not_json_output(dir_with_venv_installed: Path) -> None: ) -def test_cli_with_json_output(dir_with_venv_installed: Path) -> None: - with run_within_dir(dir_with_venv_installed): +def test_cli_with_json_output(project_builder: ToolSpecificProjectBuilder) -> None: + with run_within_dir(project_builder("example_project", "poetry install --no-interaction --no-root")): result = subprocess.run(shlex.split("poetry run deptry . -o deptry.json"), capture_output=True, text=True) # Assert that we still write to console when generating a JSON report. diff --git a/tests/cli/test_cli_arg_types.py b/tests/functional/cli/test_cli_arg_types.py similarity index 100% rename from tests/cli/test_cli_arg_types.py rename to tests/functional/cli/test_cli_arg_types.py diff --git a/tests/cli/test_cli_gitignore.py b/tests/functional/cli/test_cli_gitignore.py similarity index 53% rename from tests/cli/test_cli_gitignore.py rename to tests/functional/cli/test_cli_gitignore.py index fc3ba956..88c1dc88 100644 --- a/tests/cli/test_cli_gitignore.py +++ b/tests/functional/cli/test_cli_gitignore.py @@ -1,33 +1,19 @@ from __future__ import annotations import shlex -import shutil -import subprocess from typing import TYPE_CHECKING -import pytest from click.testing import CliRunner from deptry.cli import deptry from tests.utils import get_issues_report, run_within_dir if TYPE_CHECKING: - from pathlib import Path + from tests.functional.types import ToolSpecificProjectBuilder - from _pytest.tmpdir import TempPathFactory - -@pytest.fixture(scope="session") -def dir_with_gitignore(tmp_path_factory: TempPathFactory) -> Path: - tmp_path_proj = tmp_path_factory.getbasetemp() / "project_with_gitignore" - shutil.copytree("tests/data/project_with_gitignore", str(tmp_path_proj)) - with run_within_dir(tmp_path_proj): - assert subprocess.check_call(shlex.split("pip install .")) == 0 - return tmp_path_proj - - -def test_cli_gitignore_is_used(dir_with_gitignore: Path) -> None: - with run_within_dir(dir_with_gitignore): +def test_cli_gitignore_is_used(pip_project_builder: ToolSpecificProjectBuilder) -> None: + with run_within_dir(pip_project_builder("project_with_gitignore")): result = CliRunner().invoke(deptry, shlex.split(". -o report.json")) assert result.exit_code == 1 @@ -39,8 +25,8 @@ def test_cli_gitignore_is_used(dir_with_gitignore: Path) -> None: } -def test_cli_gitignore_is_not_used(dir_with_gitignore: Path) -> None: - with run_within_dir(dir_with_gitignore): +def test_cli_gitignore_is_not_used(pip_project_builder: ToolSpecificProjectBuilder) -> None: + with run_within_dir(pip_project_builder("project_with_gitignore")): result = CliRunner().invoke(deptry, ". --exclude build/|src/bar.py -o report.json") assert result.exit_code == 1 diff --git a/tests/functional/cli/test_cli_pdm.py b/tests/functional/cli/test_cli_pdm.py new file mode 100644 index 00000000..9f0f2081 --- /dev/null +++ b/tests/functional/cli/test_cli_pdm.py @@ -0,0 +1,24 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from click.testing import CliRunner + +from deptry.cli import deptry +from tests.utils import get_issues_report, run_within_dir + +if TYPE_CHECKING: + from tests.functional.types import ToolSpecificProjectBuilder + + +def test_cli_with_pdm(pdm_project_builder: ToolSpecificProjectBuilder) -> None: + with run_within_dir(pdm_project_builder("project_with_pdm")): + result = CliRunner().invoke(deptry, ". -o report.json") + + assert result.exit_code == 1 + assert get_issues_report() == { + "misplaced_dev": ["black"], + "missing": ["white"], + "obsolete": ["isort", "requests"], + "transitive": [], + } diff --git a/tests/functional/cli/test_cli_pep_621.py b/tests/functional/cli/test_cli_pep_621.py new file mode 100644 index 00000000..5111cda7 --- /dev/null +++ b/tests/functional/cli/test_cli_pep_621.py @@ -0,0 +1,24 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from click.testing import CliRunner + +from deptry.cli import deptry +from tests.utils import get_issues_report, run_within_dir + +if TYPE_CHECKING: + from tests.functional.types import ToolSpecificProjectBuilder + + +def test_cli_with_pep_621(pip_project_builder: ToolSpecificProjectBuilder) -> None: + with run_within_dir(pip_project_builder("pep_621_project")): + result = CliRunner().invoke(deptry, ". -o report.json") + + assert result.exit_code == 1 + assert get_issues_report() == { + "misplaced_dev": [], + "missing": ["white"], + "obsolete": ["isort", "requests", "mypy", "pytest"], + "transitive": [], + } diff --git a/tests/functional/cli/test_cli_pyproject_different_directory.py b/tests/functional/cli/test_cli_pyproject_different_directory.py new file mode 100644 index 00000000..c451791a --- /dev/null +++ b/tests/functional/cli/test_cli_pyproject_different_directory.py @@ -0,0 +1,24 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from click.testing import CliRunner + +from deptry.cli import deptry +from tests.utils import get_issues_report, run_within_dir + +if TYPE_CHECKING: + from tests.functional.types import ToolSpecificProjectBuilder + + +def test_cli_with_pyproject_different_directory(pip_project_builder: ToolSpecificProjectBuilder) -> None: + with run_within_dir(pip_project_builder("project_with_pyproject_different_directory", cwd="a_sub_directory")): + result = CliRunner().invoke(deptry, "src --config a_sub_directory/pyproject.toml -o report.json") + + assert result.exit_code == 1 + assert get_issues_report() == { + "misplaced_dev": [], + "missing": ["white"], + "obsolete": ["isort", "requests", "mypy", "pytest"], + "transitive": [], + } diff --git a/tests/cli/test_cli_requirements_txt.py b/tests/functional/cli/test_cli_requirements_txt.py similarity index 50% rename from tests/cli/test_cli_requirements_txt.py rename to tests/functional/cli/test_cli_requirements_txt.py index 76209505..95e5b617 100644 --- a/tests/cli/test_cli_requirements_txt.py +++ b/tests/functional/cli/test_cli_requirements_txt.py @@ -1,41 +1,18 @@ from __future__ import annotations -import shlex -import shutil -import subprocess from typing import TYPE_CHECKING -import pytest from click.testing import CliRunner from deptry.cli import deptry from tests.utils import get_issues_report, run_within_dir if TYPE_CHECKING: - from pathlib import Path + from tests.functional.types import ToolSpecificProjectBuilder - from _pytest.tmpdir import TempPathFactory - -@pytest.fixture(scope="session") -def requirements_txt_dir_with_venv_installed(tmp_path_factory: TempPathFactory) -> Path: - tmp_path_proj = tmp_path_factory.getbasetemp() / "project_with_requirements_txt" - shutil.copytree("tests/data/project_with_requirements_txt", str(tmp_path_proj)) - with run_within_dir(tmp_path_proj): - assert ( - subprocess.check_call( - shlex.split( - "python -m pip install -r requirements.txt -r requirements-dev.txt -r requirements-2.txt -r" - " requirements-typing.txt" - ) - ) - == 0 - ) - return tmp_path_proj - - -def test_cli_single_requirements_txt(requirements_txt_dir_with_venv_installed: Path) -> None: - with run_within_dir(requirements_txt_dir_with_venv_installed): +def test_cli_single_requirements_txt(requirements_txt_project_builder: ToolSpecificProjectBuilder) -> None: + with run_within_dir(requirements_txt_project_builder("project_with_requirements_txt")): result = CliRunner().invoke( deptry, ". --requirements-txt requirements.txt --requirements-txt-dev requirements-dev.txt -o report.json", @@ -50,8 +27,8 @@ def test_cli_single_requirements_txt(requirements_txt_dir_with_venv_installed: P } -def test_cli_multiple_requirements_txt(requirements_txt_dir_with_venv_installed: Path) -> None: - with run_within_dir(requirements_txt_dir_with_venv_installed): +def test_cli_multiple_requirements_txt(requirements_txt_project_builder: ToolSpecificProjectBuilder) -> None: + with run_within_dir(requirements_txt_project_builder("project_with_requirements_txt")): result = CliRunner().invoke( deptry, ( diff --git a/tests/functional/cli/test_cli_src_directory.py b/tests/functional/cli/test_cli_src_directory.py new file mode 100644 index 00000000..13578111 --- /dev/null +++ b/tests/functional/cli/test_cli_src_directory.py @@ -0,0 +1,24 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from click.testing import CliRunner + +from deptry.cli import deptry +from tests.utils import get_issues_report, run_within_dir + +if TYPE_CHECKING: + from tests.functional.types import ToolSpecificProjectBuilder + + +def test_cli_with_src_directory(pip_project_builder: ToolSpecificProjectBuilder) -> None: + with run_within_dir(pip_project_builder("project_with_src_directory")): + result = CliRunner().invoke(deptry, "src -o report.json") + + assert result.exit_code == 1 + assert get_issues_report() == { + "misplaced_dev": [], + "missing": ["httpx", "white"], + "obsolete": ["isort", "requests", "mypy", "pytest"], + "transitive": [], + } diff --git a/tests/functional/conftest.py b/tests/functional/conftest.py new file mode 100644 index 00000000..582ffadc --- /dev/null +++ b/tests/functional/conftest.py @@ -0,0 +1,79 @@ +from __future__ import annotations + +import shlex +import shutil +import subprocess +from pathlib import Path +from typing import TYPE_CHECKING + +import pytest + +from tests.utils import run_within_dir + +if TYPE_CHECKING: + from _pytest.tmpdir import TempPathFactory + + from tests.functional.types import ProjectBuilder, ToolSpecificProjectBuilder + + +def _build_project(root_directory: Path, project: str, setup_command: str, cwd: str | None = None) -> Path: + project_path = root_directory / project + + # If the fixture was already called with the same parameters, the project is already bootstrapped. + if project_path.exists(): + return project_path + + shutil.copytree(Path("tests/data") / project, project_path) + + with run_within_dir(project_path): + assert subprocess.check_call(shlex.split(setup_command), cwd=cwd) == 0 + + return project_path + + +@pytest.fixture(scope="session") +def project_builder(tmp_path_factory: TempPathFactory) -> ProjectBuilder: + def _project_builder(project: str, setup_command: str, cwd: str | None = None) -> Path: + return _build_project(tmp_path_factory.getbasetemp(), project, setup_command, cwd) + + return _project_builder + + +@pytest.fixture(scope="session") +def poetry_project_builder(tmp_path_factory: TempPathFactory) -> ToolSpecificProjectBuilder: + def _project_builder(project: str, cwd: str | None = None) -> Path: + return _build_project(tmp_path_factory.getbasetemp(), project, "poetry install --no-interaction --no-root", cwd) + + return _project_builder + + +@pytest.fixture(scope="session") +def pip_project_builder(tmp_path_factory: TempPathFactory) -> ToolSpecificProjectBuilder: + def _project_builder(project: str, cwd: str | None = None) -> Path: + return _build_project(tmp_path_factory.getbasetemp(), project, "pip install .", cwd) + + return _project_builder + + +@pytest.fixture(scope="session") +def pdm_project_builder(tmp_path_factory: TempPathFactory) -> ToolSpecificProjectBuilder: + def _project_builder(project: str, cwd: str | None = None) -> Path: + return _build_project(tmp_path_factory.getbasetemp(), project, "pip install pdm; pdm install", cwd) + + return _project_builder + + +@pytest.fixture(scope="session") +def requirements_txt_project_builder(tmp_path_factory: TempPathFactory) -> ToolSpecificProjectBuilder: + def _project_builder(project: str, cwd: str | None = None) -> Path: + return _build_project( + tmp_path_factory.getbasetemp(), + project, + ( + "python -m pip install -r requirements.txt -r requirements-dev.txt -r requirements-2.txt -r" + " requirements-typing.txt" + ), + cwd, + ) + + return _project_builder diff --git a/tests/functional/types.py b/tests/functional/types.py new file mode 100644 index 00000000..7796ff73 --- /dev/null +++ b/tests/functional/types.py @@ -0,0 +1,16 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Protocol + +if TYPE_CHECKING: + from pathlib import Path + + +class ProjectBuilder(Protocol): + def __call__(self, project: str, setup_command: str, cwd: str | None) -> Path: + ... + + +class ToolSpecificProjectBuilder(Protocol): + def __call__(self, project: str, cwd: str | None = None) -> Path: + ... diff --git a/tests/imports/__init__.py b/tests/unit/__init__.py similarity index 100% rename from tests/imports/__init__.py rename to tests/unit/__init__.py diff --git a/tests/issues_finder/__init__.py b/tests/unit/dependency_getter/__init__.py similarity index 100% rename from tests/issues_finder/__init__.py rename to tests/unit/dependency_getter/__init__.py diff --git a/tests/dependency_getter/test_pdm.py b/tests/unit/dependency_getter/test_pdm.py similarity index 100% rename from tests/dependency_getter/test_pdm.py rename to tests/unit/dependency_getter/test_pdm.py diff --git a/tests/dependency_getter/test_pep_621.py b/tests/unit/dependency_getter/test_pep_621.py similarity index 100% rename from tests/dependency_getter/test_pep_621.py rename to tests/unit/dependency_getter/test_pep_621.py diff --git a/tests/dependency_getter/test_poetry.py b/tests/unit/dependency_getter/test_poetry.py similarity index 100% rename from tests/dependency_getter/test_poetry.py rename to tests/unit/dependency_getter/test_poetry.py diff --git a/tests/dependency_getter/test_requirements_txt.py b/tests/unit/dependency_getter/test_requirements_txt.py similarity index 100% rename from tests/dependency_getter/test_requirements_txt.py rename to tests/unit/dependency_getter/test_requirements_txt.py diff --git a/tests/unit/imports/__init__.py b/tests/unit/imports/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/imports/test_extract.py b/tests/unit/imports/test_extract.py similarity index 100% rename from tests/imports/test_extract.py rename to tests/unit/imports/test_extract.py diff --git a/tests/unit/issues_finder/__init__.py b/tests/unit/issues_finder/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/issues_finder/test_misplaced_dev.py b/tests/unit/issues_finder/test_misplaced_dev.py similarity index 100% rename from tests/issues_finder/test_misplaced_dev.py rename to tests/unit/issues_finder/test_misplaced_dev.py diff --git a/tests/issues_finder/test_missing.py b/tests/unit/issues_finder/test_missing.py similarity index 100% rename from tests/issues_finder/test_missing.py rename to tests/unit/issues_finder/test_missing.py diff --git a/tests/issues_finder/test_obsolete.py b/tests/unit/issues_finder/test_obsolete.py similarity index 100% rename from tests/issues_finder/test_obsolete.py rename to tests/unit/issues_finder/test_obsolete.py diff --git a/tests/issues_finder/test_transitive.py b/tests/unit/issues_finder/test_transitive.py similarity index 100% rename from tests/issues_finder/test_transitive.py rename to tests/unit/issues_finder/test_transitive.py diff --git a/tests/test_config.py b/tests/unit/test_config.py similarity index 100% rename from tests/test_config.py rename to tests/unit/test_config.py diff --git a/tests/test_core.py b/tests/unit/test_core.py similarity index 100% rename from tests/test_core.py rename to tests/unit/test_core.py diff --git a/tests/test_dependency.py b/tests/unit/test_dependency.py similarity index 100% rename from tests/test_dependency.py rename to tests/unit/test_dependency.py diff --git a/tests/test_dependency_specification_detector.py b/tests/unit/test_dependency_specification_detector.py similarity index 100% rename from tests/test_dependency_specification_detector.py rename to tests/unit/test_dependency_specification_detector.py diff --git a/tests/test_json_writer.py b/tests/unit/test_json_writer.py similarity index 100% rename from tests/test_json_writer.py rename to tests/unit/test_json_writer.py diff --git a/tests/test_module.py b/tests/unit/test_module.py similarity index 100% rename from tests/test_module.py rename to tests/unit/test_module.py diff --git a/tests/test_python_file_finder.py b/tests/unit/test_python_file_finder.py similarity index 100% rename from tests/test_python_file_finder.py rename to tests/unit/test_python_file_finder.py diff --git a/tests/test_result_logger.py b/tests/unit/test_result_logger.py similarity index 100% rename from tests/test_result_logger.py rename to tests/unit/test_result_logger.py diff --git a/tests/test_utils.py b/tests/unit/test_utils.py similarity index 100% rename from tests/test_utils.py rename to tests/unit/test_utils.py