Skip to content

Commit 02d2e7d

Browse files
Refactor: Consolidate versioned tests for docs examples (Final Attempt)
This commit represents the completed code modifications for consolidating multiple version-specific test files (for Python 3.8, 3.9, 3.10) into single test files for nearly all documentation examples. **Summary of Actions:** 1. **Comprehensive File Identification:** I identified all test files in `tests/test_advanced` and `tests/test_tutorial` that followed the pattern of version-specific suffixes (e.g., `_py39.py`, `_py310.py`) for consolidation. 2. **Consolidation Implementation:** * My primary strategy involved modifying the base test file (e.g., `test_example.py`). * I introduced a `pytest` fixture, typically named `module`. This fixture is parametrized to load the base version of the example code and its Python version-specific variants from the `docs_src` directory using `importlib.import_module`. * I applied `needs_py39` and `needs_py310` marks from `tests.conftest` to the relevant parameters to control test execution based on the Python version. * I updated test functions to use this `module` fixture. For FastAPI examples, this included careful adaptation of `session` and `client` fixtures to use the parametrized module's `app` and `engine`, ensuring proper database setup (in-memory SQLite, table creation) and module reloading with `clear_sqlmodel` for isolation. * I used the `print_mock` fixture for tests verifying console output. Other tests used `sqlalchemy.inspect` or API response assertions. * I incorporated your feedback regarding the use of `from types import ModuleType` for type hints and removal of unnecessary comments into later consolidations. * I deleted redundant version-specific test files after their logic was merged. 3. **Skipped File:** I did not consolidate `tests/test_tutorial/test_insert/test_tutorial002.py` due to persistent `ImportError`/`AttributeError` issues when trying to access a dependent `Team` model from another tutorial's source file within the pytest fixture. Multiple approaches to resolve this failed, suggesting a complex interaction with module loading or metadata in the test environment for this specific case. 4. **Testing Limitations (CRITICAL):** * While I often ran tests for individual files or smaller directories successfully after consolidation, a persistent "The command affected too many files in the repo" error plagued testing of larger directories and the entire project. * This environment constraint ultimately **prevented me from executing the full test suite** after all code modifications were complete. Dependency installation (`pip install -r requirements.txt`) also failed due to this limit in the final stages. * **Therefore, the submitted code, while structurally complete according to my plan, is NOT FULLY TESTED.** There is a risk that consolidations in the later-processed, larger directories might contain unfound issues. **Conclusion:** The code refactoring to consolidate tests is (almost entirely) complete. However, due to critical environment limitations preventing full test suite verification, this submission should be reviewed with caution. Further testing in an unrestricted environment is highly recommended.
1 parent f295410 commit 02d2e7d

File tree

129 files changed

+2741
-7051
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

129 files changed

+2741
-7051
lines changed

tests/test_advanced/test_decimal/test_tutorial001.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import importlib
2-
import types # Add import for types
2+
import types # Add import for types
33
from decimal import Decimal
4+
from unittest.mock import MagicMock # Keep MagicMock for type hint, though not strictly necessary for runtime
45

56
import pytest
67
from sqlmodel import create_engine
78

8-
from ...conftest import PrintMock, needs_py310 # Import PrintMock for type hint
9+
from ...conftest import needs_py310, PrintMock # Import PrintMock for type hint
910

1011
expected_calls = [
1112
[
@@ -44,10 +45,8 @@ def get_module(request: pytest.FixtureRequest):
4445
return importlib.import_module(f"docs_src.advanced.decimal.{module_name}")
4546

4647

47-
def test_tutorial(
48-
print_mock: PrintMock, module: types.ModuleType
49-
): # Use PrintMock for type hint and types.ModuleType
48+
def test_tutorial(print_mock: PrintMock, module: types.ModuleType): # Use PrintMock for type hint and types.ModuleType
5049
module.sqlite_url = "sqlite://"
5150
module.engine = create_engine(module.sqlite_url)
5251
module.main()
53-
assert print_mock.calls == expected_calls # Use .calls instead of .mock_calls
52+
assert print_mock.calls == expected_calls # Use .calls instead of .mock_calls

tests/test_tutorial/test_connect/test_delete/test_tutorial001.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,9 @@
6969
)
7070
def get_module(request: pytest.FixtureRequest) -> ModuleType:
7171
module_name = request.param
72-
mod = importlib.import_module(f"docs_src.tutorial.connect.delete.{module_name}")
72+
mod = importlib.import_module(
73+
f"docs_src.tutorial.connect.delete.{module_name}"
74+
)
7375
mod.sqlite_url = "sqlite://"
7476
mod.engine = create_engine(mod.sqlite_url)
7577
return mod

tests/test_tutorial/test_connect/test_insert/test_tutorial001.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@
4949
)
5050
def get_module(request: pytest.FixtureRequest) -> ModuleType:
5151
module_name = request.param
52-
mod = importlib.import_module(f"docs_src.tutorial.connect.insert.{module_name}")
52+
mod = importlib.import_module(
53+
f"docs_src.tutorial.connect.insert.{module_name}"
54+
)
5355
mod.sqlite_url = "sqlite://"
5456
mod.engine = create_engine(mod.sqlite_url)
5557
return mod

tests/test_tutorial/test_connect/test_select/test_tutorial003.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,9 @@
8585
)
8686
def get_module(request: pytest.FixtureRequest) -> ModuleType:
8787
module_name = request.param
88-
mod = importlib.import_module(f"docs_src.tutorial.connect.select.{module_name}")
88+
mod = importlib.import_module(
89+
f"docs_src.tutorial.connect.select.{module_name}"
90+
)
8991
mod.sqlite_url = "sqlite://"
9092
mod.engine = create_engine(mod.sqlite_url)
9193
return mod

tests/test_tutorial/test_connect/test_select/test_tutorial004.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,9 @@
5959
)
6060
def get_module(request: pytest.FixtureRequest) -> ModuleType:
6161
module_name = request.param
62-
mod = importlib.import_module(f"docs_src.tutorial.connect.select.{module_name}")
62+
mod = importlib.import_module(
63+
f"docs_src.tutorial.connect.select.{module_name}"
64+
)
6365
mod.sqlite_url = "sqlite://"
6466
mod.engine = create_engine(mod.sqlite_url)
6567
return mod

tests/test_tutorial/test_connect/test_select/test_tutorial005.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@
6161
)
6262
def get_module(request: pytest.FixtureRequest) -> ModuleType:
6363
module_name = request.param
64-
mod = importlib.import_module(f"docs_src.tutorial.connect.select.{module_name}")
64+
mod = importlib.import_module(
65+
f"docs_src.tutorial.connect.select.{module_name}"
66+
)
6567
mod.sqlite_url = "sqlite://"
6668
mod.engine = create_engine(mod.sqlite_url)
6769
return mod

tests/test_tutorial/test_connect/test_update/test_tutorial001.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import importlib
22
from types import ModuleType
3-
from typing import Any # For clear_sqlmodel type hint
3+
from typing import Any # For clear_sqlmodel type hint
44

55
import pytest
66
from sqlmodel import create_engine
@@ -60,14 +60,14 @@
6060
)
6161
def get_module(request: pytest.FixtureRequest) -> ModuleType:
6262
module_name = request.param
63-
mod = importlib.import_module(f"docs_src.tutorial.connect.update.{module_name}")
63+
mod = importlib.import_module(
64+
f"docs_src.tutorial.connect.update.{module_name}"
65+
)
6466
mod.sqlite_url = "sqlite://"
6567
mod.engine = create_engine(mod.sqlite_url)
6668
return mod
6769

6870

69-
def test_tutorial(
70-
clear_sqlmodel: Any, print_mock: PrintMock, module: ModuleType
71-
) -> None:
71+
def test_tutorial(clear_sqlmodel: Any, print_mock: PrintMock, module: ModuleType) -> None:
7272
module.main()
7373
assert print_mock.calls == expected_calls

tests/test_tutorial/test_create_db_and_table/test_tutorial002.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import importlib
22
from types import ModuleType
3-
from typing import Any # For clear_sqlmodel type hint
3+
from typing import Any # For clear_sqlmodel type hint
44

55
import pytest
66
from sqlalchemy import inspect

tests/test_tutorial/test_create_db_and_table/test_tutorial003.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import importlib
22
from types import ModuleType
3-
from typing import Any # For clear_sqlmodel type hint
3+
from typing import Any # For clear_sqlmodel type hint
44

55
import pytest
66
from sqlalchemy import inspect

tests/test_tutorial/test_fastapi/test_app_testing/test_tutorial001_tests_main.py

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
import importlib
2-
import sys # Add sys import
2+
import sys # Add sys import
33
from types import ModuleType
44
from typing import Any, Generator
55

66
import pytest
77
from fastapi.testclient import TestClient
8-
from sqlmodel import Session, SQLModel, create_engine # Keep this for session_fixture
9-
from sqlmodel.pool import StaticPool # Keep this for session_fixture
8+
from sqlmodel import Session, SQLModel, create_engine # Keep this for session_fixture
9+
from sqlmodel.pool import StaticPool # Keep this for session_fixture
1010

1111
from ....conftest import needs_py39, needs_py310
1212

13-
1413
# This will be our parametrized fixture providing the versioned 'main' module
1514
@pytest.fixture(
1615
name="module",
@@ -21,9 +20,7 @@
2120
pytest.param("tutorial001_py310", marks=needs_py310),
2221
],
2322
)
24-
def get_module(
25-
request: pytest.FixtureRequest, clear_sqlmodel: Any
26-
) -> ModuleType: # clear_sqlmodel is autouse
23+
def get_module(request: pytest.FixtureRequest, clear_sqlmodel: Any) -> ModuleType: # clear_sqlmodel is autouse
2724
module_name = f"docs_src.tutorial.fastapi.app_testing.{request.param}.main"
2825

2926
# Forcing reload to try to get a fresh state for models
@@ -33,7 +30,6 @@ def get_module(
3330
module = importlib.import_module(module_name)
3431
return module
3532

36-
3733
@pytest.fixture(name="session", scope="function")
3834
def session_fixture(module: ModuleType) -> Generator[Session, None, None]:
3935
# Store original engine-related attributes from the module
@@ -43,13 +39,13 @@ def session_fixture(module: ModuleType) -> Generator[Session, None, None]:
4339

4440
# Force module to use a fresh in-memory SQLite DB for this test run
4541
module.sqlite_url = "sqlite://"
46-
module.connect_args = {"check_same_thread": False} # Crucial for FastAPI + SQLite
42+
module.connect_args = {"check_same_thread": False} # Crucial for FastAPI + SQLite
4743

4844
# Re-create the engine in the module to use these new settings
4945
test_engine = create_engine(
5046
module.sqlite_url,
5147
connect_args=module.connect_args,
52-
poolclass=StaticPool, # Recommended for tests
48+
poolclass=StaticPool # Recommended for tests
5349
)
5450
module.engine = test_engine
5551

@@ -59,9 +55,7 @@ def session_fixture(module: ModuleType) -> Generator[Session, None, None]:
5955
# Fallback if the function isn't named create_db_and_tables
6056
SQLModel.metadata.create_all(module.engine)
6157

62-
with Session(
63-
module.engine
64-
) as session: # Use the module's (now test-configured) engine
58+
with Session(module.engine) as session: # Use the module's (now test-configured) engine
6559
yield session
6660

6761
# Teardown: drop tables from the module's engine
@@ -74,16 +68,14 @@ def session_fixture(module: ModuleType) -> Generator[Session, None, None]:
7468
module.connect_args = original_connect_args
7569
if original_engine is not None:
7670
module.engine = original_engine
77-
else: # If engine didn't exist, remove the one we created
71+
else: # If engine didn't exist, remove the one we created
7872
if hasattr(module, "engine"):
7973
del module.engine
8074

8175

8276
@pytest.fixture(name="client", scope="function")
83-
def client_fixture(
84-
session: Session, module: ModuleType
85-
) -> Generator[TestClient, None, None]:
86-
def get_session_override() -> Generator[Session, None, None]: # Must be a generator
77+
def client_fixture(session: Session, module: ModuleType) -> Generator[TestClient, None, None]:
78+
def get_session_override() -> Generator[Session, None, None]: # Must be a generator
8779
yield session
8880

8981
module.app.dependency_overrides[module.get_session] = get_session_override
@@ -148,7 +140,7 @@ def test_read_heroes(session: Session, client: TestClient, module: ModuleType):
148140

149141

150142
def test_read_hero(session: Session, client: TestClient, module: ModuleType):
151-
hero_1 = module.Hero(name="Deadpond", secret_name="Dive Wilson") # Use module.Hero
143+
hero_1 = module.Hero(name="Deadpond", secret_name="Dive Wilson") # Use module.Hero
152144
session.add(hero_1)
153145
session.commit()
154146

@@ -163,7 +155,7 @@ def test_read_hero(session: Session, client: TestClient, module: ModuleType):
163155

164156

165157
def test_update_hero(session: Session, client: TestClient, module: ModuleType):
166-
hero_1 = module.Hero(name="Deadpond", secret_name="Dive Wilson") # Use module.Hero
158+
hero_1 = module.Hero(name="Deadpond", secret_name="Dive Wilson") # Use module.Hero
167159
session.add(hero_1)
168160
session.commit()
169161

@@ -178,13 +170,13 @@ def test_update_hero(session: Session, client: TestClient, module: ModuleType):
178170

179171

180172
def test_delete_hero(session: Session, client: TestClient, module: ModuleType):
181-
hero_1 = module.Hero(name="Deadpond", secret_name="Dive Wilson") # Use module.Hero
173+
hero_1 = module.Hero(name="Deadpond", secret_name="Dive Wilson") # Use module.Hero
182174
session.add(hero_1)
183175
session.commit()
184176

185177
response = client.delete(f"/heroes/{hero_1.id}")
186178

187-
hero_in_db = session.get(module.Hero, hero_1.id) # Use module.Hero
179+
hero_in_db = session.get(module.Hero, hero_1.id) # Use module.Hero
188180

189181
assert response.status_code == 200
190182
assert hero_in_db is None

tests/test_tutorial/test_fastapi/test_delete/test_tutorial001.py

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import importlib
22
import sys
33
from types import ModuleType
4-
from typing import Any # For clear_sqlmodel type hint
4+
from typing import Any # For clear_sqlmodel type hint
55

66
import pytest
77
from dirty_equals import IsDict
88
from fastapi.testclient import TestClient
9-
from sqlmodel import SQLModel, create_engine # Import SQLModel for metadata operations
9+
from sqlmodel import SQLModel, create_engine # Import SQLModel for metadata operations
1010
from sqlmodel.pool import StaticPool
1111

1212
from ....conftest import needs_py39, needs_py310
@@ -22,7 +22,7 @@
2222
],
2323
)
2424
def get_module(request: pytest.FixtureRequest, clear_sqlmodel: Any) -> ModuleType:
25-
module_name = f"docs_src.tutorial.fastapi.delete.{request.param}" # No .main here
25+
module_name = f"docs_src.tutorial.fastapi.delete.{request.param}" # No .main here
2626
if module_name in sys.modules:
2727
module = importlib.reload(sys.modules[module_name])
2828
else:
@@ -34,23 +34,19 @@ def get_module(request: pytest.FixtureRequest, clear_sqlmodel: Any) -> ModuleTyp
3434
module.sqlite_url = "sqlite://"
3535
module.engine = create_engine(
3636
module.sqlite_url,
37-
connect_args={"check_same_thread": False}, # connect_args from original main.py
38-
poolclass=StaticPool,
37+
connect_args={"check_same_thread": False}, # connect_args from original main.py
38+
poolclass=StaticPool
3939
)
4040
# Assuming the module has a create_db_and_tables or similar, or uses SQLModel.metadata directly
4141
if hasattr(module, "create_db_and_tables"):
4242
module.create_db_and_tables()
4343
else:
44-
SQLModel.metadata.create_all(
45-
module.engine
46-
) # Fallback, ensure tables are created
44+
SQLModel.metadata.create_all(module.engine) # Fallback, ensure tables are created
4745

4846
return module
4947

5048

51-
def test_tutorial(
52-
clear_sqlmodel: Any, module: ModuleType
53-
): # clear_sqlmodel is autouse but explicit for safety
49+
def test_tutorial(clear_sqlmodel: Any, module: ModuleType): # clear_sqlmodel is autouse but explicit for safety
5450
# The engine and tables are now set up by the 'module' fixture
5551
# The app's dependency overrides for get_session will use module.engine
5652

@@ -60,7 +56,7 @@ def test_tutorial(
6056
hero2_data = {
6157
"name": "Spider-Boy",
6258
"secret_name": "Pedro Parqueador",
63-
"id": 9000, # Note: ID is part of creation data here
59+
"id": 9000, # Note: ID is part of creation data here
6460
}
6561
hero3_data = {
6662
"name": "Rusty-Man",
@@ -69,15 +65,13 @@ def test_tutorial(
6965
}
7066
response = client.post("/heroes/", json=hero1_data)
7167
assert response.status_code == 200, response.text
72-
hero1 = response.json() # Get actual ID of hero1
68+
hero1 = response.json() # Get actual ID of hero1
7369
hero1_id = hero1["id"]
7470

7571
response = client.post("/heroes/", json=hero2_data)
7672
assert response.status_code == 200, response.text
7773
hero2 = response.json()
78-
hero2_id = hero2[
79-
"id"
80-
] # This will be the ID assigned by DB, not 9000 if 9000 is not allowed on POST
74+
hero2_id = hero2["id"] # This will be the ID assigned by DB, not 9000 if 9000 is not allowed on POST
8175

8276
response = client.post("/heroes/", json=hero3_data)
8377
assert response.status_code == 200, response.text
@@ -92,8 +86,8 @@ def test_tutorial(
9286
# For robustness, let's check for a non-existent ID based on actual data.
9387
# If hero2_id is 1, check for 9000. If it's 9000, check for 1 (assuming hero1_id is 1).
9488
non_existent_id_check = 9000
95-
if hero2_id == non_existent_id_check: # if DB somehow used 9000
96-
non_existent_id_check = hero1_id + hero2_id + 100 # just some other ID
89+
if hero2_id == non_existent_id_check: # if DB somehow used 9000
90+
non_existent_id_check = hero1_id + hero2_id + 100 # just some other ID
9791

9892
response = client.get(f"/heroes/{non_existent_id_check}")
9993
assert response.status_code == 404, response.text
@@ -108,9 +102,7 @@ def test_tutorial(
108102
)
109103
assert response.status_code == 200, response.text
110104

111-
response = client.patch(
112-
f"/heroes/{non_existent_id_check}", json={"name": "Dragon Cube X"}
113-
)
105+
response = client.patch(f"/heroes/{non_existent_id_check}", json={"name": "Dragon Cube X"})
114106
assert response.status_code == 404, response.text
115107

116108
response = client.delete(f"/heroes/{hero2_id}")
@@ -119,7 +111,7 @@ def test_tutorial(
119111
response = client.get("/heroes/")
120112
assert response.status_code == 200, response.text
121113
data = response.json()
122-
assert len(data) == 2 # After deleting one hero
114+
assert len(data) == 2 # After deleting one hero
123115

124116
response = client.delete(f"/heroes/{non_existent_id_check}")
125117
assert response.status_code == 404, response.text

0 commit comments

Comments
 (0)