Files
sqlmodel/tests/test_tutorial/test_one/test_tutorial005.py
google-labs-jules[bot] 02d2e7da0a 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.
2025-06-20 07:22:35 +00:00

85 lines
3.1 KiB
Python

import importlib
import sys
import types
from typing import Any
from unittest.mock import patch
import pytest
from sqlalchemy.exc import NoResultFound # Keep this import
from sqlmodel import create_engine, SQLModel, Session, delete # Ensure Session and delete
from ...conftest import get_testing_print_function, needs_py310, PrintMock
expected_calls_tutorial005 = [
[
"Hero:",
{
"id": 1,
"name": "Test Hero",
"secret_name": "Secret Test Hero",
"age": 24,
},
]
]
@pytest.fixture(
name="module",
params=[
"tutorial005",
pytest.param("tutorial005_py310", marks=needs_py310),
],
)
def module_fixture(request: pytest.FixtureRequest, clear_sqlmodel: Any):
module_name = request.param
full_module_name = f"docs_src.tutorial.one.{module_name}"
if full_module_name in sys.modules:
mod = importlib.reload(sys.modules[full_module_name])
else:
mod = importlib.import_module(full_module_name)
mod.sqlite_url = "sqlite://"
mod.engine = create_engine(mod.sqlite_url)
# Table creation logic:
# tutorial005.py's main() attempts to select a hero, expecting NoResultFound.
# This implies the table should exist but be empty initially for that part of main().
# The create_db_and_tables() is called inside main() *after* the select that fails.
# So, the fixture should create tables.
if hasattr(mod, "SQLModel") and hasattr(mod.SQLModel, "metadata"):
mod.SQLModel.metadata.create_all(mod.engine) # Create tables
return mod
def test_tutorial(module: types.ModuleType, print_mock: PrintMock, clear_sqlmodel: Any):
# module.main() in tutorial005.py is structured to:
# 1. Try selecting a hero (expects NoResultFound).
# 2. Call create_db_and_tables().
# 3. Create a hero (this part is commented out in docs_src, but the test does it).
# The test then separately calls select_heroes().
# Phase 1: Test the NoResultFound part of main()
# The fixture already created tables, so main() trying to select might not fail with NoResultFound
# if create_db_and_tables() in main also populates.
# However, the original test has main() raise NoResultFound. This implies main() itself
# first tries a select on potentially empty (but existing) tables.
# The `clear_sqlmodel` fixture ensures the DB is clean (tables might be recreated by module_fixture).
with pytest.raises(NoResultFound):
module.main() # This should execute the part of main() that expects no results
# Phase 2: Test select_heroes() after manually adding a hero
# This part matches the original test's logic after the expected exception.
with Session(module.engine) as session:
session.exec(delete(module.Hero)) # Clear any heroes if main() somehow added them
session.add(module.Hero(name="Test Hero", secret_name="Secret Test Hero", age=24))
session.commit()
with patch("builtins.print", new=get_testing_print_function(print_mock.calls)):
module.select_heroes() # This function is defined in the tutorial module
assert print_mock.calls == expected_calls_tutorial005