Skip to content

Commit 7d8fb3a

Browse files
committed
refactor: pytest collection and CLI options
1 parent 542a8b2 commit 7d8fb3a

File tree

6 files changed

+50
-20
lines changed

6 files changed

+50
-20
lines changed

api/pyproject.toml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,13 @@ ignore = [
9191
skip = "*.lock,*.cjs"
9292
ignore-words-list = "ignored-word"
9393

94-
[tool.pytest]
95-
markers =[
94+
[tool.pytest.ini_options]
95+
# Makes pytest CLI discover markers and conftest settings:
96+
markers = [
97+
"unit: mark a test as unit test.",
9698
"integration: mark a test as integration test."
9799
]
100+
testpaths = [
101+
"src/tests/unit",
102+
"src/tests/integration"
103+
]

api/src/tests/conftest.py

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
from app import create_app
1212
from authentication.authentication import auth_with_jwt
13-
from config import config
13+
from config import config as project_config
1414
from data_providers.clients.mongodb.mongo_database_client import MongoDatabaseClient
1515
from features.todo.repository.todo_repository import TodoRepository, get_todo_repository
1616
from tests.integration.mock_authentication import mock_auth_with_jwt
@@ -28,7 +28,7 @@ def test_client():
2828

2929
@pytest.fixture(autouse=True)
3030
def disable_auth():
31-
config.AUTH_ENABLED = False
31+
project_config.AUTH_ENABLED = False
3232
os.environ["AUTH_ENABLED"] = "False"
3333

3434

@@ -76,10 +76,45 @@ async def request_body():
7676
return request
7777

7878

79-
def pytest_addoption(parser):
80-
parser.addoption("--integration", action="store_true", help="run integration tests")
79+
def pytest_configure(config: pytest.Config):
80+
"""Add markers to be recognised by pytest."""
81+
has_unit_option = config.getoption("unit", default=False)
82+
has_integration_option = config.getoption("integration", default=False)
83+
marker_expr = config.getoption("markexpr", default="")
84+
if marker_expr != "" and (has_integration_option or has_unit_option):
85+
pytest.exit("Invalid options: Cannot use --markexpr with --unit or --integration options", 4)
8186

8287

83-
def pytest_runtest_setup(item):
84-
if "integration" in item.keywords and not item.config.getoption("integration"):
85-
pytest.skip("need --integration option to run")
88+
def pytest_addoption(parser):
89+
"""Add option to pytest parser for running unit/integration tests."""
90+
parser.addoption("--unit", action="store_true", default=False, help="run unit tests")
91+
parser.addoption("--integration", action="store_true", default=False, help="run integration tests")
92+
93+
94+
def pytest_collection_modifyitems(config: pytest.Config, items: list[pytest.Item]):
95+
"""Add markers to tests based on folder structure."""
96+
unit_tests_directory = config.rootpath / "src/tests/unit"
97+
integration_tests_directory = config.rootpath / "src/tests/integration"
98+
for item in items:
99+
if item.path.is_relative_to(unit_tests_directory):
100+
item.add_marker("unit")
101+
if item.path.is_relative_to(integration_tests_directory):
102+
item.add_marker("integration")
103+
104+
105+
def pytest_runtest_setup(item: pytest.Item):
106+
"""Skip tests based on options provided."""
107+
has_unit_option = item.config.getoption("unit", default=False)
108+
has_integration_option = item.config.getoption("integration", default=False)
109+
match (has_unit_option, has_integration_option):
110+
case (False, True):
111+
# skip unit tests
112+
if "unit" in item.keywords:
113+
pytest.skip("unit tests are skipped when explicitly running integration tests")
114+
case (True, False):
115+
# skip integration tests
116+
if "integration" in item.keywords:
117+
pytest.skip("integration tests are skipped when explicitly running unit tests")
118+
case _:
119+
# run all tests
120+
return

api/src/tests/integration/common/test_exception_handler.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
1-
import pytest
21
from starlette.status import HTTP_422_UNPROCESSABLE_ENTITY
32
from starlette.testclient import TestClient
43

5-
pytestmark = pytest.mark.integration
6-
74

85
def test_exception_handler_validation_error(test_app: TestClient):
96
response = test_app.post("/todos", json={"title": 1})

api/src/tests/integration/features/health_check/test_health_check_feature.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
1-
import pytest
21
from starlette.status import HTTP_200_OK
32
from starlette.testclient import TestClient
43

5-
pytestmark = pytest.mark.integration
6-
74

85
class TestTodo:
96
def test_get(self, test_app: TestClient):

api/src/tests/integration/features/todo/test_todo_feature.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88

99
from data_providers.clients.client_interface import ClientInterface
1010

11-
pytestmark = pytest.mark.integration
12-
1311

1412
class TestTodo:
1513
@pytest.fixture(autouse=True)

api/src/tests/integration/features/whoami/test_whoami_feature.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
1-
import pytest
21
from starlette.status import HTTP_200_OK
32
from starlette.testclient import TestClient
43

54
from authentication.models import User
65
from config import config
76
from tests.integration.mock_authentication import get_mock_jwt_token
87

9-
pytestmark = pytest.mark.integration
10-
118

129
class TestWhoami:
1310
def test_whoami(self, test_app: TestClient):

0 commit comments

Comments
 (0)