""" App 端根级 fixture:Appium Driver 实例 + 失败自动截图。 提供: app_config — session 级 App 配置 driver — Appium WebDriver(每个用例独立) platform — 当前平台 android / ios Usage: def test_app_login(driver, platform): driver.find_element(AppiumBy.ACCESSIBILITY_ID, "login_btn").click() """ from datetime import datetime from pathlib import Path import pytest SCREENSHOTS_DIR = Path("output/screenshots") @pytest.fixture(scope="session") def app_config(config): """session 级:App 配置段。""" return config["app"] @pytest.fixture def platform(app_config) -> str: """当前执行的平台(默认 android,后续可参数化)。""" return app_config.get("platforms", ["android"])[0] @pytest.fixture def driver(app_config, platform, request): """每个用例独立的 Appium driver,失败自动截图。 注意:运行前需确保 Appium Server 已启动(appium --log-level info)。 """ try: from appium import webdriver from appium.options.android import UiAutomator2Options from appium.options.ios import XCUITestOptions except ImportError: pytest.skip("Appium-Python-Client 未安装,跳过 App 测试。") appium_host = app_config.get("appium_host", "http://localhost:4723") screenshot_on_failure = app_config.get("screenshot_on_failure", True) driver = None try: if platform == "android": android_cfg = app_config["android"] options = UiAutomator2Options() options.device_name = android_cfg["device_name"] options.app_package = android_cfg["app_package"] options.app_activity = android_cfg["app_activity"] options.no_reset = android_cfg.get("no_reset", True) options.new_command_timeout = android_cfg.get("new_command_timeout", 120) options.automation_name = "UiAutomator2" else: ios_cfg = app_config["ios"] options = XCUITestOptions() options.device_name = ios_cfg["device_name"] options.bundle_id = ios_cfg["bundle_id"] options.no_reset = ios_cfg.get("no_reset", True) options.new_command_timeout = ios_cfg.get("new_command_timeout", 120) options.automation_name = "XCUITest" driver = webdriver.Remote(appium_host, options=options) yield driver # ── 失败自动截图 ── if ( screenshot_on_failure and hasattr(request.node, "rep_call") and request.node.rep_call.failed and driver is not None ): scenario_dir = SCREENSHOTS_DIR / request.node.name scenario_dir.mkdir(parents=True, exist_ok=True) ts = datetime.now().strftime("%Y%m%d_%H%M%S") path = str(scenario_dir / f"FAIL_{platform}_{ts}.png") driver.save_screenshot(path) finally: if driver is not None: driver.quit() # ── pytest 报告 hook ── @pytest.hookimpl(tryfirst=True, hookwrapper=True) def pytest_runtest_makereport(item, call): """记录用例执行结果,供失败截图使用。""" outcome = yield rep = outcome.get_result() setattr(item, f"rep_{rep.when}", rep)