Download conftest.py from jun2024/fc: direct link, hf CLI and curl.
- Browser
- Download file 9.74 kB
-
https://huggingface.co/spaces/jun2024/fc/resolve/main/conftest.py
- Command line
-
hf download hf://spaces/jun2024/fc/conftest.py
-
curl -L -o conftest.py https://huggingface.co/spaces/jun2024/fc/resolve/main/conftest.py
9.74 kB
| """ | |
| Pytest 配置文件 | |
| 提供测试 fixtures | |
| """ | |
| import os | |
| import sys | |
| from typing import Any, Generator, Iterator | |
| import django | |
| import pytest | |
| from apps.core.utils.path import Path | |
| # 添加项目路径 | |
| sys.path.insert(0, str(Path(__file__).parent / "apiSystem")) | |
| # 设置 Django 配置 | |
| os.environ.setdefault("DJANGO_SETTINGS_MODULE", "apiSystem.settings") | |
| # 排除脚本文件(非标准 pytest 测试) | |
| collect_ignore = [ | |
| "tests/unit/automation/test_court_document.py", | |
| ] | |
| def django_db_setup(django_db_setup: Any, django_db_blocker: Any) -> Any: | |
| """ | |
| 设置测试数据库 | |
| 确保测试使用独立的测试数据库,不影响生产数据库 | |
| """ | |
| with django_db_blocker.unblock(): | |
| # pytest-django 会自动创建测试数据库 | |
| # 这里只需要确保使用正确的数据库配置 | |
| from django.conf import settings | |
| db_cfg = settings.DATABASES.get("default", {}) | |
| db_engine = str(db_cfg.get("ENGINE", "")) | |
| db_name = str(db_cfg.get("NAME", "")) | |
| if db_engine == "django.db.backends.sqlite3": | |
| is_test_db = ( | |
| "test_" in db_name | |
| or "_test" in db_name | |
| or ":memory:" in db_name | |
| or "memorydb" in db_name | |
| or db_name == ":memory:" | |
| ) | |
| else: | |
| lowered_name = db_name.lower() | |
| is_test_db = ( | |
| lowered_name.startswith("test_") | |
| or lowered_name.endswith("_test") | |
| or "_test_" in lowered_name | |
| or lowered_name == "test" | |
| ) | |
| assert is_test_db, f"错误:测试正在使用生产数据库 {db_name}!测试已中止。" | |
| yield | |
| # 测试结束后,pytest-django 会自动清理测试数据库 | |
| def django_db_modify_db_settings() -> None: | |
| """ | |
| 修改测试数据库设置 | |
| 默认使用 PostgreSQL 测试库,也支持显式切换 SQLite。 | |
| """ | |
| from django.conf import settings | |
| inferred_engine = "sqlite" if (os.environ.get("DATABASE_PATH") or os.environ.get("TEST_DB_PATH")) else "postgresql" | |
| test_db_engine = (os.environ.get("TEST_DB_ENGINE") or os.environ.get("DB_ENGINE") or inferred_engine).strip().lower() | |
| if test_db_engine in ("sqlite", "sqlite3", "django.db.backends.sqlite3"): | |
| test_db_path = (os.environ.get("TEST_DB_PATH") or os.environ.get("DATABASE_PATH") or ":memory:").strip() | |
| settings.DATABASES["default"] = { | |
| "ENGINE": "django.db.backends.sqlite3", | |
| "NAME": test_db_path, | |
| "ATOMIC_REQUESTS": False, | |
| "CONN_MAX_AGE": 0, | |
| "TIME_ZONE": "Asia/Shanghai", | |
| "OPTIONS": { | |
| "timeout": 20, | |
| }, | |
| } | |
| return | |
| raw_test_password = os.environ.get("TEST_DB_PASSWORD") | |
| raw_db_password = os.environ.get("DB_PASSWORD") | |
| resolved_password = raw_test_password if raw_test_password is not None else raw_db_password | |
| if resolved_password is None: | |
| resolved_password = "postgres" | |
| settings.DATABASES["default"] = { | |
| "ENGINE": "django.db.backends.postgresql", | |
| "NAME": (os.environ.get("TEST_DB_NAME") or "fachuan_test").strip(), | |
| "USER": (os.environ.get("TEST_DB_USER") or os.environ.get("DB_USER") or "postgres").strip(), | |
| "PASSWORD": resolved_password.strip(), | |
| "HOST": (os.environ.get("TEST_DB_HOST") or os.environ.get("DB_HOST") or "127.0.0.1").strip(), | |
| "PORT": int((os.environ.get("TEST_DB_PORT") or os.environ.get("DB_PORT") or "5432").strip()), | |
| "ATOMIC_REQUESTS": False, | |
| "CONN_MAX_AGE": 0, | |
| "TIME_ZONE": "Asia/Shanghai", | |
| "CONN_HEALTH_CHECKS": True, | |
| } | |
| def api_client() -> Any: | |
| """提供 API 测试客户端""" | |
| from django.test import Client | |
| return Client() | |
| def authenticated_client(db: Any) -> Any: | |
| """提供已认证的测试客户端""" | |
| from django.test import Client | |
| from apps.organization.models import LawFirm, Lawyer | |
| client = Client() | |
| firm = LawFirm.objects.create(name="测试律所") | |
| user = Lawyer.objects.create_user( | |
| username="testuser", | |
| password="testpass123", | |
| is_admin=True, | |
| law_firm=firm, | |
| ) | |
| client.force_login(user) | |
| return client | |
| def law_firm(db: Any) -> Any: | |
| """提供测试律所""" | |
| from apps.organization.models import LawFirm | |
| return LawFirm.objects.create(name="Fixture测试律所") | |
| def lawyer(db: Any, law_firm: Any) -> Any: | |
| """提供测试律师""" | |
| from apps.organization.models import Lawyer | |
| return Lawyer.objects.create_user( | |
| username="fixturelawyer", | |
| password="testpass123", | |
| real_name="Fixture律师", | |
| law_firm=law_firm, | |
| ) | |
| def admin_lawyer(db: Any, law_firm: Any) -> Any: | |
| """提供管理员律师""" | |
| from apps.organization.models import Lawyer | |
| return Lawyer.objects.create_user( | |
| username="adminlawyer", | |
| password="testpass123", | |
| is_admin=True, | |
| law_firm=law_firm, | |
| ) | |
| def client_entity(db: Any) -> Any: | |
| """提供测试客户""" | |
| from apps.client.models import Client | |
| return Client.objects.create( | |
| name="Fixture测试客户", | |
| client_type=Client.NATURAL, | |
| is_our_client=True, | |
| ) | |
| def contract(db: Any, lawyer: Any) -> Any: | |
| """提供测试合同""" | |
| from apps.contracts.models import Contract | |
| return Contract.objects.create( | |
| name="Fixture测试合同", | |
| case_type="civil", | |
| ) | |
| def case(db: Any, contract: Any) -> Any: | |
| """提供测试案件""" | |
| from apps.cases.models import Case | |
| return Case.objects.create( | |
| name="Fixture测试案件", | |
| contract=contract, | |
| ) | |
| # ========== Hypothesis 配置 ========== | |
| from hypothesis import Verbosity, settings | |
| # 配置 Hypothesis | |
| settings.register_profile("default", max_examples=5, verbosity=Verbosity.normal) | |
| settings.register_profile("ci", max_examples=1000, verbosity=Verbosity.verbose) | |
| settings.register_profile("dev", max_examples=10, verbosity=Verbosity.verbose) | |
| settings.register_profile("debug", max_examples=10, verbosity=Verbosity.debug) | |
| # 加载配置(默认使用 default) | |
| profile = os.getenv("HYPOTHESIS_PROFILE", "default") | |
| settings.load_profile(profile) | |
| # ========== 测试工具 Fixtures ========== | |
| def mock_contract_service() -> Any: | |
| """提供 Mock 合同服务""" | |
| from tests.mocks import MockContractService | |
| return MockContractService() | |
| def mock_case_service() -> Any: | |
| """提供 Mock 案件服务""" | |
| from tests.mocks import MockCaseService | |
| return MockCaseService() | |
| def mock_permission_service() -> Any: | |
| """提供 Mock 权限服务""" | |
| from tests.mocks import MockPermissionService | |
| return MockPermissionService() | |
| def mock_email_service() -> Any: | |
| """提供 Mock 邮件服务""" | |
| from tests.mocks import MockEmailService | |
| return MockEmailService() | |
| def query_counter(db: Any) -> Any: | |
| """ | |
| 查询计数器 | |
| 用于测试数据库查询优化,检测 N+1 查询问题 | |
| 使用方法: | |
| with query_counter() as counter: | |
| # 执行操作 | |
| cases = Case.objects.select_related('contract').all() | |
| list(cases) | |
| assert counter.count <= 1 # 断言查询次数 | |
| """ | |
| from django.db import connection | |
| from django.test.utils import CaptureQueriesContext | |
| class QueryCounter: | |
| def __init__(self) -> None: | |
| self.context: CaptureQueriesContext | None = None | |
| self.count: int = 0 | |
| def __enter__(self) -> "QueryCounter": | |
| self.context = CaptureQueriesContext(connection) | |
| self.context.__enter__() | |
| return self | |
| def __exit__(self, *args: Any) -> None: | |
| assert self.context is not None | |
| self.context.__exit__(*args) | |
| self.count = len(self.context.captured_queries) | |
| def queries(self) -> list[dict[str, Any]]: | |
| """获取所有查询""" | |
| return self.context.captured_queries if self.context else [] | |
| def _counter() -> QueryCounter: | |
| return QueryCounter() | |
| return _counter | |
| def assert_num_queries(db: Any) -> Any: | |
| """ | |
| 断言查询次数 | |
| 使用方法: | |
| with assert_num_queries(1): | |
| # 执行操作,应该只有 1 次查询 | |
| cases = Case.objects.select_related('contract').all() | |
| list(cases) | |
| """ | |
| from django.db import connection | |
| from django.test.utils import CaptureQueriesContext | |
| class AssertNumQueries: | |
| def __init__(self, expected_count: int) -> None: | |
| self.expected_count = expected_count | |
| self.context: CaptureQueriesContext | None = None | |
| def __enter__(self) -> "AssertNumQueries": | |
| self.context = CaptureQueriesContext(connection) | |
| self.context.__enter__() | |
| return self | |
| def __exit__(self, *args: Any) -> None: | |
| assert self.context is not None | |
| self.context.__exit__(*args) | |
| actual_count = len(self.context.captured_queries) | |
| if actual_count != self.expected_count: | |
| queries = "\n".join(f"{i + 1}. {q['sql']}" for i, q in enumerate(self.context.captured_queries)) | |
| raise AssertionError(f"Expected {self.expected_count} queries, but got {actual_count}:\n{queries}") | |
| return AssertNumQueries | |