{"id":15009451,"url":"https://github.com/globalradio/pytest-appium","last_synced_at":"2025-04-09T17:24:33.226Z","repository":{"id":83904653,"uuid":"119971846","full_name":"GlobalRadio/pytest-appium","owner":"GlobalRadio","description":"Plugin for running Appium with pytest","archived":false,"fork":false,"pushed_at":"2024-06-05T11:33:22.000Z","size":40,"stargazers_count":15,"open_issues_count":3,"forks_count":5,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-03-23T19:38:24.200Z","etag":null,"topics":["appium","pytest","pytest-plugin","python-3","python-3-6"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/GlobalRadio.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-02-02T11:07:29.000Z","updated_at":"2023-08-08T16:04:31.000Z","dependencies_parsed_at":null,"dependency_job_id":"d1d10a18-9b04-43b6-adaf-2b9631ca76cb","html_url":"https://github.com/GlobalRadio/pytest-appium","commit_stats":{"total_commits":47,"total_committers":2,"mean_commits":23.5,"dds":"0.17021276595744683","last_synced_commit":"878ae6ad20284db43b5e63986baabe2e84c86342"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GlobalRadio%2Fpytest-appium","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GlobalRadio%2Fpytest-appium/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GlobalRadio%2Fpytest-appium/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GlobalRadio%2Fpytest-appium/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/GlobalRadio","download_url":"https://codeload.github.com/GlobalRadio/pytest-appium/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248075614,"owners_count":21043623,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["appium","pytest","pytest-plugin","python-3","python-3-6"],"created_at":"2024-09-24T19:25:23.082Z","updated_at":"2025-04-09T17:24:33.210Z","avatar_url":"https://github.com/GlobalRadio.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"pytest-appium\n=============\n\n`pytest-appium` is a plugin for [pytest](https://docs.pytest.org/en/latest/) that provides support for running [Appium](http://appium.io/) based tests.\n\nIt is inspired by [pytest-selenium](https://github.com/pytest-dev/pytest-selenium)\n\nThis package has a suite of additional utilities\n\n* HTML Reports\n* Augment the base Appium `driver` with additional modular functionality with python mixins.\n* Wait for Appium to be ready\n* `pytest.mark.platform`\n* Light python builders for Android [UiSelector](https://developer.android.com/reference/android/support/test/uiautomator/UiSelector.html)\n\n\nSetup\n-----\n\npip requirements file\n```\n    git+http://GIT.SERVER.LOCATION/pytest-appium.git@master#egg=pytest-appium\n```\n\npytest.ini\n```\n    [pytest]\n    addopts = -p pytest_appium\n```\n\npython test example\n```python\n    from pytest_appium.android.UIAutomator2 import UiSelector, UiScrollable\n    from pytest_appium.driver.proxy._enums import Direction\n\n    @pytest.mark.platform('ios')\n    def test_example(appium_extended, platform):\n        el = appium_extended.find_element_on_page(\n            UiSelector().resourceIdMatches('.*awesome_item'),\n            direction=Direction.LEFT,\n        )\n        assert el.text == 'Example'\n```\n\ncommandline example\n```bash\n    $(PYTHON_VENV)/bin/py.test \\\n        $(TEST_PATH) \\\n        --html=$(REPORT_PATH)/report.html \\\n        --junitxml=$(REPORT_PATH)/report.xml \\\n        --variables variables/pytest/android_emulator_local.json \\\n        --capability app $(APPIUM_APK_PATH)\n```\nfor more commandline details use `pytest --help` under the heading `appium`\n\n\nFeatures\n--------\n\n### HTML reports\n\n[pytest-html](https://pypi.python.org/pypi/pytest-html/) for Appium tests.\n\nIncludes screenshot, page `XML` and full `logcat` dumps.\n\n\n### Wait for startup conditions\n\nOrchestration of service startup order is sometimes problematic.\nOn startup we can wait for a varity of conditions.\n`pytest --help` for details\n\n```\n    --appium_wait_for_condition={appium,android_device_available}\n                            Type of appium condition to wait for before beginning\n                            first test\n    --appium_wait_for_seconds=APPIUM_WAIT_FOR_SECONDS\n                            Seconds to wait for an Appium server to become\n                            available before raising an error.\n```\n\n* `appium` - Waits for the Appium port to become active\n* `android_device_available` - Trys to send a low level `wd` api call to launch an Android packagename \"NOT-REAL\". We know an android device is connected and available if the error message explicitly contains \"NOT-REAL\".\n\nExample of use in a `docker-compose.yml` waiting for android device.\n```yaml\n    command:\n      - pytest\n      ...\n      - --appium_host=android-container\n      - --appium_wait_for_seconds=90\n      - --appium_wait_for_condition=android_device_available\n      ...\n```\n\nExampe of use in a `bash` script waiting for appium service to become active.\n```bash\n    ...\n    (nohup node $(APPIUM_PATH) $(APPIUM_ARGS) \u003e $(REPORT_FOLDER)/appium.log \u0026)\n    ...\n\n    _env/bin/py.test \\\n        tests \\\n        ...\n        --appium_host=localhost \\\n        --appium_wait_for_condition appium \\\n        --appium_wait_for_seconds 30 \\\n        ...\n\n```\n\n### pytest Markers for platforms\n\n```python\n    @pytest.mark.platform('ios')\n    def test_ios(appium_extended):\n        pass\n\n    @pytest.mark.platform('android')\n    def test_android(appium_extended):\n        pass\n\n    def test_both_ios_and_android(appium_extended):\n        pass\n```\n\n### Python wrapper for expressing UiSelector syntax in python\n\nHandling long android strings to compose UiSelectors is inflexible. A lightweight UiSelector python builder is provided\n\n```python\n    from pytest_appium.android.UIAutomator2 import UiSelector, UiScrollable\n\n    # Create a UiSelector python builder\n    selector = UiSelector().resourceIdMatches('.*filmstrip_list').childSelector(UiSelector().index(1))\n    assert str(selector) == 'new UiSelector().resourceIdMatches(\".*filmstrip_list\").childSelector(new UiSelector().index(1))'\n\n    # UiSelector objects can be used directly\n    el = self.driver.find_element_by_android_uiautomator(selector)\n\n    # UiSelectors can have segments modified/appended after creation\n    selector.ui_objects[-1].childSelector(UiSelector().className('my_class'))\n    assert str(selector) == 'new UiSelector().resourceIdMatches(\".*filmstrip_list\").childSelector(new UiSelector().index(1).childSelector(new UiSelector().className(\"my_class\")))'\n    self.driver.find_element_by_android_uiautomator(selector)\n```\n\n### Appium Driver Extension Framework\n\nThe base Appium [python-client](https://github.com/appium/python-client) `driver` supports base functions.\nWe sometimes want to add extra functionality to this `driver` for individual platforms.\n\nWe can transparently overlay extra Mixin's over the base `driver` object. This is available as the pytest fixture `appium_extended`.\n\n```python\n    from pytest_appium.driver.proxy.proxy_mixin import register_proxy_mixin\n\n    @register_proxy_mixin(name='android')\n    class MyAndroidDriverMixin():\n        def new_thing(self, text):\n            log.debug('my mixin method for android')\n            # modify the selector in some cool way\n            return self.find_element(*my_cool_selector_with_text)\n\n    @register_proxy_mixin(name='ios')\n    class MyIOSDriverMixin():\n        def new_thing(self, text):\n            log.debug('my mixin method for ios')\n            # modify the selector in some cool way\n            return self.find_element(*my_cool_selector_with_text)\n\n    def test_my_mixin(appium_extended):\n        el = appium_extended.new_thing('text of awesome')\n\n    # The `appium` fixture can be used to get the base `driver` without mixin augmentation.\n    @pytest.mark.xfail(strict=True)\n    def test_my_mixin_without_extension(appium):\n        el = appium.new_thing('text of awesome')\n\n```\n\nOmit the `name='platform'` argument to allow the mixin to augment all platforms.\n\nNote: `dir(appium_extended)` will *NOT* reveal your additional mixin methods. They are invisible. (This could be improved in a future version)\n\n\n### `appium_extended` Driver Extensions Summary\n\n| platform | method | description |\n|----------|--------|-------------|\n| all | .platform | return a string of the platform name (android, ios) |\n| all | .wait_for() | wait for element to become visible |\n| all | .find_element_safe | same as find_element but returns None rather than throw exception |\n| all | .get_element_bounds | return dict of derived location information of element |\n| all | .swipe_element | swipe in a direction |\n| all | .find_element_on_page | swipe up/down left/right looking for an element. Similar to android UiScrollable but platform dependent |\n| android | .find_element_by_android_uiautomator | accepts UiSelector python objects |\n| android | .scroll_to_element_by_android_uiautomator | similar to find_element_on_page |\n| android | .back, .home, .app_switcher, .background_app | send android keycodes |\n| android | .wait_for_webview | wait for a webview to become active and populated |\n| ios | | in progress |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fglobalradio%2Fpytest-appium","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fglobalradio%2Fpytest-appium","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fglobalradio%2Fpytest-appium/lists"}