Skip to main content

Appium integration for the tquality test automation framework, built on tquality-py-core.

Project description

tquality-py-appium

Appium integration for the tquality test automation framework, built on tquality-py-core.

Page-object style mobile UI testing for Android and iOS: typed elements with built-in smart waits, a dependency-injection composition root, cascading JSON5 config, native/webview switching, and Allure reporting with screencasts.

Русская версия

Installation

pip install tquality-py-appium
# or
uv add tquality-py-appium

Requires Python >= 3.12 and a running Appium server (local or remote grid / cloud such as BrowserStack, SauceLabs).

Quickstart

1. Generate config files

# config.json5 - framework behavior defaults (timeouts, context, screencast)
tquality-appium-config init

# capabilities.json5 - devices + applications
tquality-appium-config caps-init

2. Describe your devices and apps

capabilities.json5 (json5: comments and trailing commas allowed):

{
    "$schema": "https://cdn.jsdelivr.net/gh/Tquality-ru/tquality-py-appium@master/schema/capabilities.schema.json",
    "selectedDevice": "local_android",
    "selectedApplication": "demo_app",
    "devices": {
        "local_android": {
            "location": "http://127.0.0.1:4723",
            "capabilities": {
                "appium:platformName": "Android",
                "appium:automationName": "UiAutomator2",
                "appium:deviceName": "emulator",
                "appium:autoGrantPermissions": true,
            },
        },
    },
    "applications": {
        "demo_app": {
            "appium:appPackage": "com.example.shop",
            "appium:appActivity": "com.example.shop.MainActivity",
        },
    },
}

The active device/app pair is picked via selectedDevice / selectedApplication, or overridden at runtime — handy for a CI device matrix:

TEST_SELECTED_DEVICE=pixel_8 TEST_SELECTED_APPLICATION=demo_app_prod pytest

3. Wire up the composition root

conftest.py at your project root registers the DI container once:

from pathlib import Path

from tquality_appium import AppiumServices

AppiumServices.setup(config_dir=Path(__file__).resolve().parent)

A per-test fixture starts and tears down the Appium session:

# tests/conftest.py
from collections.abc import Iterator

import pytest

from tquality_appium import AppiumDriverService, AppiumServices


@pytest.fixture(autouse=True)
def appium_session() -> Iterator[AppiumDriverService]:
    svc = AppiumServices.driver()
    try:
        yield svc
    finally:
        svc.quit()
        AppiumServices.driver.reset()
        AppiumServices.logger.reset()
        AppiumServices.waiter.reset()

Key features

Page objects with BaseForm

A screen is a class; its unique_element defines "this screen is shown". Tests call business methods, never raw elements:

from tquality_appium import BaseForm, By


class LoginForm(BaseForm):
    def __init__(self) -> None:
        super().__init__(
            unique_element=self.element_factory.label(
                By.xpath('//*[@text="Sign in"]'), "Login form",
            ),
            name="Login form",
        )
        self._phone = self.element_factory.input(
            By.accessibility_id("phone_input"), "Phone number",
        )
        self._submit = self.element_factory.button(
            By.accessibility_id("login_button"), "Login",
        )

    def login(self, phone: str) -> None:
        self._phone.type_text(phone)
        self._submit.click()

Typed elements and locator strategies

element_factory builds Button / Input / Label / CheckBox. The By factory covers cross-platform and native strategies:

from tquality_appium import By, UiScrollable, UiSelector

By.accessibility_id("login_button")
By.xpath('//XCUIElementTypeButton[@name="Continue"]')
By.ios_predicate("name == 'Login'")

# Android UiAutomator via a fluent builder
By.android_uiautomator(UiSelector().resource_id("com.example.shop:id/ok"))

# Scroll until the element comes into view
By.android_uiautomator(
    UiScrollable().scroll_into_view(UiSelector().text_contains("Checkout"))
)

Elements expose intent-level actions — click(), type_text(), check() / toggle(), .text, .is_displayed — each with built-in waits.

Smart waits and element state

Every interaction waits for the right precondition first. Each element carries an ElementState (e.g. buttons default to CLICKABLE, labels to DISPLAYED); override per element when the default gets in the way:

from tquality_appium import By, ElementState

# A tab whose unique element reports displayed == false — only require it to exist
self._tab = self.element_factory.button(
    By.xpath('//*[contains(@resource-id, "tab_profile")]'),
    "Profile tab",
    state=ElementState.EXISTS_IN_ANY_STATE,
)

# Explicit waits are available when you need them
self._submit.wait.until_visible(timeout=10)
assert LoginForm().wait_for_displayed(raise_on_timeout=True)

Allure steps with screencast

@step records an Allure step. At LogLevel.WITH_SCREENCAST the framework captures video for that step (native start_recording_screen, with an automatic webm fallback when on-device recording is blocked). Page source is attached to the report on failure.

import allure

from tquality_appium import LogLevel, step

from myapp.screens.home_page import HomePage
from myapp.screens.login_form import LoginForm


class TestNavigation:
    @allure.title("Profile tab opens the login form for a guest")
    @step("Open the profile tab", LogLevel.WITH_SCREENCAST)
    def test_profile_requires_login(self) -> None:
        home = HomePage()
        assert home.wait_for_displayed(), "Home not displayed on launch"
        home.navigation_menu.open_profile()
        assert LoginForm().wait_for_displayed(), "Login form not displayed"

Native ↔ webview, alerts and windows

ContextManager handles native/webview switching; context.wait.for_alert waits on system dialogs (permission prompts, iOS ATT, etc.):

from tquality_appium import AppiumServices, ContextManager

ctx = AppiumServices.get_service(ContextManager)
with ctx.context("WEBVIEW_com.example.shop"):
    ...  # interact inside the embedded webview

# Wait for a system permission dialog and accept it
alert = AppiumServices.driver().context.wait.for_alert(
    lambda a: "Allow" in a.text,
    message="permission prompt",
)
if alert:
    alert.accept()

Collections → Pydantic models

Pull a whole UI list into typed models in a single round-trip (via execute_driver_script, with an automatic per-field fallback if the server disables it):

from pydantic import BaseModel

from tquality_appium import AppiumServices, By, CollectionFactory, DomField


class Product(BaseModel):
    title: str = DomField.id("com.example.shop:id/title")
    price: str = DomField.id("com.example.shop:id/price")


factory = AppiumServices.get_service(CollectionFactory)
products = factory.from_container(Product, By.class_name("android.widget.ListView"))

Dependency-injection composition root

AppiumServices is the single composition root. Resolve services by type (rename-safe), or subclass to add/replace services:

from dependency_injector import providers

from tquality_appium import AppiumServices, ContextManager


class ProjectServices(AppiumServices):
    my_service = providers.Singleton(MyService)


# Resolve by type, not by provider name
ctx = AppiumServices.get_service(ContextManager)

Cascading JSON5 config and per-test devices

config.json5 (framework behavior) and capabilities.json5 (infrastructure) are resolved upward from the test's directory to the workspace root. This lets tests/ios/ and tests/android/ each ship their own capabilities.json5 (iOS simulator vs Android emulator) while common settings live at the root — configs are rebuilt per test automatically.

Documentation

See tquality-py-core for the driver-agnostic concepts (BaseConfig, Logger, BaseForm, BaseElement, JSON-schema cascading config) — everything from core is re-exported here.

Appium-specific:

  • AppiumConfig — framework behavior (timeouts, default context, screencast)
  • CapabilitiesConfig — devices + applications, loaded from capabilities.json5
  • AppiumDriverService — manages the appium.webdriver.Remote session
  • ContextManager / ContextWaiter — native/webview switching, alerts, windows
  • ElementFactory — typed element creation (Button / Input / Label / CheckBox)
  • CollectionFactory + DomField — extract list[PydanticModel] from UI lists
  • ExecuteDriver — batched commands via execute_driver_script
  • AppiumServices — DI composition root

License

Apache-2.0. See LICENSE and NOTICE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tquality_py_appium-0.1.8.tar.gz (102.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tquality_py_appium-0.1.8-py3-none-any.whl (92.0 kB view details)

Uploaded Python 3

File details

Details for the file tquality_py_appium-0.1.8.tar.gz.

File metadata

  • Download URL: tquality_py_appium-0.1.8.tar.gz
  • Upload date:
  • Size: 102.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for tquality_py_appium-0.1.8.tar.gz
Algorithm Hash digest
SHA256 5ac4f47bf2b0eb19ac387e5271ba8a46fc81a3d5e4474a0f71924157776ff3ba
MD5 d86a1d18257850431945c9c96bd1f00b
BLAKE2b-256 d160dd9019d05d908f7b1a1c63c2d75d0fb3aac006f08f33909cbe04690e7d50

See more details on using hashes here.

File details

Details for the file tquality_py_appium-0.1.8-py3-none-any.whl.

File metadata

  • Download URL: tquality_py_appium-0.1.8-py3-none-any.whl
  • Upload date:
  • Size: 92.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for tquality_py_appium-0.1.8-py3-none-any.whl
Algorithm Hash digest
SHA256 0ef77701dccd3567a0ce79575a888a0cfa4b7eb645894d20f2a86322a9af1689
MD5 a3c2ae7272b021cc257d0a3e6855feae
BLAKE2b-256 acf65d5aca78614609c4802b0d01aedd4bc9d2e64f1053aa9cc3254034ca8088

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page