appium-pytest-kit 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- appium_pytest_kit-0.1.0/PKG-INFO +372 -0
- appium_pytest_kit-0.1.0/README.md +344 -0
- appium_pytest_kit-0.1.0/pyproject.toml +75 -0
- appium_pytest_kit-0.1.0/setup.cfg +4 -0
- appium_pytest_kit-0.1.0/src/appium_pytest_kit/__init__.py +37 -0
- appium_pytest_kit-0.1.0/src/appium_pytest_kit/_internal/__init__.py +4 -0
- appium_pytest_kit-0.1.0/src/appium_pytest_kit/_internal/device_resolver.py +240 -0
- appium_pytest_kit-0.1.0/src/appium_pytest_kit/_internal/diagnostics.py +46 -0
- appium_pytest_kit-0.1.0/src/appium_pytest_kit/_internal/reporting.py +45 -0
- appium_pytest_kit-0.1.0/src/appium_pytest_kit/_internal/server.py +77 -0
- appium_pytest_kit-0.1.0/src/appium_pytest_kit/_internal/video.py +50 -0
- appium_pytest_kit-0.1.0/src/appium_pytest_kit/_version.py +11 -0
- appium_pytest_kit-0.1.0/src/appium_pytest_kit/actions.py +141 -0
- appium_pytest_kit-0.1.0/src/appium_pytest_kit/cli.py +234 -0
- appium_pytest_kit-0.1.0/src/appium_pytest_kit/driver.py +93 -0
- appium_pytest_kit-0.1.0/src/appium_pytest_kit/errors.py +64 -0
- appium_pytest_kit-0.1.0/src/appium_pytest_kit/hooks.py +36 -0
- appium_pytest_kit-0.1.0/src/appium_pytest_kit/interfaces.py +30 -0
- appium_pytest_kit-0.1.0/src/appium_pytest_kit/py.typed +0 -0
- appium_pytest_kit-0.1.0/src/appium_pytest_kit/pytest_plugin.py +339 -0
- appium_pytest_kit-0.1.0/src/appium_pytest_kit/settings.py +131 -0
- appium_pytest_kit-0.1.0/src/appium_pytest_kit/waits.py +157 -0
- appium_pytest_kit-0.1.0/src/appium_pytest_kit.egg-info/PKG-INFO +372 -0
- appium_pytest_kit-0.1.0/src/appium_pytest_kit.egg-info/SOURCES.txt +26 -0
- appium_pytest_kit-0.1.0/src/appium_pytest_kit.egg-info/dependency_links.txt +1 -0
- appium_pytest_kit-0.1.0/src/appium_pytest_kit.egg-info/entry_points.txt +5 -0
- appium_pytest_kit-0.1.0/src/appium_pytest_kit.egg-info/requires.txt +7 -0
- appium_pytest_kit-0.1.0/src/appium_pytest_kit.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: appium-pytest-kit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Reusable Appium 2.x + pytest mobile test framework
|
|
5
|
+
Author: appium-pytest-kit contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/gianlucasoare/appium-pytest-kit
|
|
8
|
+
Project-URL: Documentation, https://github.com/gianlucasoare/appium-pytest-kit#readme
|
|
9
|
+
Project-URL: Repository, https://github.com/gianlucasoare/appium-pytest-kit.git
|
|
10
|
+
Keywords: appium,pytest,mobile,automation,framework
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Framework :: Pytest
|
|
19
|
+
Classifier: Topic :: Software Development :: Testing
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
Requires-Dist: Appium-Python-Client>=4.0.0
|
|
23
|
+
Requires-Dist: pydantic-settings>=2.3.0
|
|
24
|
+
Requires-Dist: pytest>=8.2.0
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: ruff>=0.9.0; extra == "dev"
|
|
27
|
+
Requires-Dist: pytest-cov>=6.0.0; extra == "dev"
|
|
28
|
+
|
|
29
|
+
# appium-pytest-kit
|
|
30
|
+
|
|
31
|
+
`appium-pytest-kit` is a reusable Appium 2.x + pytest framework library for Python 3.11+.
|
|
32
|
+
|
|
33
|
+
- `pip install appium-pytest-kit` (or install from GitHub — see below)
|
|
34
|
+
- `appium-pytest-kit-init` to bootstrap configuration, or `--framework` to scaffold a full project
|
|
35
|
+
- Write tests immediately with built-in fixtures and zero boilerplate
|
|
36
|
+
|
|
37
|
+
**Full documentation:** [DOCUMENTATION.md](./DOCUMENTATION.md)
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
### From PyPI (once published)
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install appium-pytest-kit
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### From GitHub
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# latest main branch
|
|
53
|
+
pip install git+https://github.com/gianlucasoare/appium-pytest-kit.git
|
|
54
|
+
|
|
55
|
+
# specific branch
|
|
56
|
+
pip install git+https://github.com/gianlucasoare/appium-pytest-kit.git@main
|
|
57
|
+
|
|
58
|
+
# specific tag
|
|
59
|
+
pip install git+https://github.com/gianlucasoare/appium-pytest-kit.git@v0.1.0
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Local clone (editable, for development)
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
git clone https://github.com/gianlucasoare/appium-pytest-kit.git
|
|
66
|
+
cd appium-pytest-kit
|
|
67
|
+
pip install -e ".[dev]"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Quickstart
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
python -m venv .venv
|
|
76
|
+
source .venv/bin/activate
|
|
77
|
+
pip install git+https://github.com/gianlucasoare/appium-pytest-kit.git
|
|
78
|
+
appium-pytest-kit-init # creates .env with starter config
|
|
79
|
+
# or scaffold a full project:
|
|
80
|
+
appium-pytest-kit-init --framework --root my-project
|
|
81
|
+
pytest -q
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Edit `.env` with your device and app details, then write tests.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Step-by-step: test a real app in 5 minutes
|
|
89
|
+
|
|
90
|
+
This example tests the Android Calculator on an emulator. See [DOCUMENTATION.md](./DOCUMENTATION.md) for the full iOS walkthrough and all options.
|
|
91
|
+
|
|
92
|
+
### 1. Start Appium and an emulator
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
appium &
|
|
96
|
+
emulator -avd Pixel_7_API_33 &
|
|
97
|
+
adb devices # confirm emulator-5554 is listed
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### 2. Configure `.env`
|
|
101
|
+
|
|
102
|
+
```env
|
|
103
|
+
APP_PLATFORM=android
|
|
104
|
+
APP_APPIUM_URL=http://127.0.0.1:4723
|
|
105
|
+
APP_DEVICE_NAME=emulator-5554
|
|
106
|
+
APP_PLATFORM_VERSION=13
|
|
107
|
+
APP_APP_PACKAGE=com.google.android.calculator
|
|
108
|
+
APP_APP_ACTIVITY=com.android.calculator2.Calculator
|
|
109
|
+
APP_NO_RESET=true
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### 3. Write a test
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
# tests/test_calculator.py
|
|
116
|
+
import pytest
|
|
117
|
+
from appium.webdriver.common.appiumby import AppiumBy
|
|
118
|
+
|
|
119
|
+
BTN_2 = (AppiumBy.ACCESSIBILITY_ID, "2")
|
|
120
|
+
BTN_PLUS = (AppiumBy.ACCESSIBILITY_ID, "plus")
|
|
121
|
+
BTN_3 = (AppiumBy.ACCESSIBILITY_ID, "3")
|
|
122
|
+
BTN_EQUALS = (AppiumBy.ACCESSIBILITY_ID, "equals")
|
|
123
|
+
RESULT = (AppiumBy.RESOURCE_ID, "com.google.android.calculator:id/result_final")
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@pytest.mark.integration
|
|
127
|
+
def test_addition(actions):
|
|
128
|
+
actions.tap(BTN_2)
|
|
129
|
+
actions.tap(BTN_PLUS)
|
|
130
|
+
actions.tap(BTN_3)
|
|
131
|
+
actions.tap(BTN_EQUALS)
|
|
132
|
+
assert actions.text(RESULT) == "5"
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### 4. Run it
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
pytest -m integration -v
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Built-in fixtures
|
|
144
|
+
|
|
145
|
+
| Fixture | Scope | Description |
|
|
146
|
+
|---|---|---|
|
|
147
|
+
| `settings` | session | Resolved `AppiumPytestKitSettings` — access any config field |
|
|
148
|
+
| `appium_server` | session | Server URL and whether it is framework-managed |
|
|
149
|
+
| `driver` | function | Live `appium.webdriver.Remote`, quit automatically after each test |
|
|
150
|
+
| `waiter` | function | Explicit waits with `WaitTimeoutError` on timeout |
|
|
151
|
+
| `actions` | function | High-level UI helpers: `tap`, `type_text`, `text`, `exists`, `swipe`, and more |
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## Session modes
|
|
156
|
+
|
|
157
|
+
Control driver lifecycle per test or across the whole session:
|
|
158
|
+
|
|
159
|
+
```env
|
|
160
|
+
APP_SESSION_MODE=clean # default: fresh driver per test
|
|
161
|
+
APP_SESSION_MODE=clean-session # shared driver, app reset between tests
|
|
162
|
+
APP_SESSION_MODE=debug # shared driver, no reset (fast local debugging)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Device resolution (3-tier priority)
|
|
168
|
+
|
|
169
|
+
1. **Explicit** — `APP_DEVICE_NAME` / `APP_UDID` set in `.env` or CLI
|
|
170
|
+
2. **Profile** — `APP_DEVICE_PROFILE=pixel7` resolved from `data/devices.yaml`
|
|
171
|
+
3. **Auto-detect** — `adb devices` (Android) or `xcrun simctl` / `xctrace` (iOS)
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
# Use a named profile from data/devices.yaml
|
|
175
|
+
pytest --app-device-profile pixel7
|
|
176
|
+
|
|
177
|
+
# Auto-detect (no device settings needed)
|
|
178
|
+
pytest
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Failure diagnostics
|
|
184
|
+
|
|
185
|
+
On test failure the framework automatically captures:
|
|
186
|
+
- **Screenshot** → `artifacts/screenshots/<test_id>.png`
|
|
187
|
+
- **Page source** → `artifacts/pagesource/<test_id>.xml`
|
|
188
|
+
- **Video** (if policy allows) → `artifacts/videos/<test_id>.mp4`
|
|
189
|
+
|
|
190
|
+
```env
|
|
191
|
+
APP_VIDEO_POLICY=never # default
|
|
192
|
+
APP_VIDEO_POLICY=failed # record and save only on failure
|
|
193
|
+
APP_VIDEO_POLICY=always # record every test
|
|
194
|
+
APP_ARTIFACTS_DIR=artifacts
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Allure attachments are added automatically when `allure-pytest` is installed.
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Configuration
|
|
202
|
+
|
|
203
|
+
Settings are loaded from `.env` → environment variables → CLI flags (highest wins).
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
pytest --app-platform ios
|
|
207
|
+
pytest --app-device-name "Pixel 7" --app-platform-version 13
|
|
208
|
+
pytest --appium-url http://192.168.1.10:4723
|
|
209
|
+
pytest --app-app-package com.example.app --app-app-activity .MainActivity
|
|
210
|
+
pytest --app-session-mode clean-session
|
|
211
|
+
pytest --app-device-profile pixel7
|
|
212
|
+
pytest --app-video-policy failed
|
|
213
|
+
pytest --app-is-simulator
|
|
214
|
+
pytest --app-capabilities-json '{"autoGrantPermissions": true}'
|
|
215
|
+
pytest --app-manage-appium-server # start Appium automatically
|
|
216
|
+
pytest --app-reporting-enabled # write artifacts/appium-pytest-kit/summary.json
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
See [DOCUMENTATION.md § Configuration](./DOCUMENTATION.md#5-configuration) for the full settings table.
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Expanded waits
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
waiter.for_clickable(locator) # wait for element to be tappable
|
|
227
|
+
waiter.for_invisibility(locator) # wait for element to disappear
|
|
228
|
+
waiter.for_text_contains(locator, "partial text") # wait for text substring
|
|
229
|
+
waiter.for_text_equals(locator, "exact text") # wait for exact text match
|
|
230
|
+
waiter.for_all_visible([loc1, loc2, loc3]) # wait for all elements to appear
|
|
231
|
+
waiter.for_any_visible([loc1, loc2]) # wait for first visible element
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## Expanded actions
|
|
237
|
+
|
|
238
|
+
```python
|
|
239
|
+
actions.tap_if_present(locator) # tap only if visible, returns bool
|
|
240
|
+
actions.clear(locator) # clear a text field
|
|
241
|
+
actions.swipe(sx, sy, ex, ey) # W3C Pointer swipe gesture
|
|
242
|
+
actions.scroll_down() # swipe up on screen center
|
|
243
|
+
actions.scroll_up() # swipe down on screen center
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## Extension hooks
|
|
249
|
+
|
|
250
|
+
Implement these in your `conftest.py` to customise behaviour without touching the framework:
|
|
251
|
+
|
|
252
|
+
```python
|
|
253
|
+
# conftest.py
|
|
254
|
+
|
|
255
|
+
def pytest_appium_pytest_kit_capabilities(capabilities, settings):
|
|
256
|
+
"""Add extra capabilities before each driver session."""
|
|
257
|
+
return {"autoGrantPermissions": True, "language": "en"}
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def pytest_appium_pytest_kit_configure_settings(settings):
|
|
261
|
+
"""Replace settings at session start."""
|
|
262
|
+
return settings.model_copy(update={"implicit_wait": 2.0})
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def pytest_appium_pytest_kit_driver_created(driver, settings):
|
|
266
|
+
"""Run setup immediately after each driver is created."""
|
|
267
|
+
driver.orientation = "PORTRAIT"
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## Project scaffolding
|
|
273
|
+
|
|
274
|
+
Generate a full project structure in one command:
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
appium-pytest-kit-init --framework --root my-project
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
Creates:
|
|
281
|
+
```
|
|
282
|
+
my-project/
|
|
283
|
+
├── data/devices.yaml # device profiles
|
|
284
|
+
├── pages/
|
|
285
|
+
│ ├── base_page.py # BasePage composition class
|
|
286
|
+
│ └── example_page.py # starter page object
|
|
287
|
+
├── flows/ # reusable multi-step flows
|
|
288
|
+
├── tests/
|
|
289
|
+
│ ├── android/test_smoke.py
|
|
290
|
+
│ └── ios/test_smoke.py
|
|
291
|
+
├── conftest.py
|
|
292
|
+
├── pytest.ini
|
|
293
|
+
└── .env.example
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## Public API
|
|
299
|
+
|
|
300
|
+
Top-level imports (stable):
|
|
301
|
+
|
|
302
|
+
```python
|
|
303
|
+
from appium_pytest_kit import (
|
|
304
|
+
AppiumPytestKitSettings,
|
|
305
|
+
AppiumPytestKitError,
|
|
306
|
+
ConfigurationError,
|
|
307
|
+
DeviceResolutionError,
|
|
308
|
+
LaunchValidationError,
|
|
309
|
+
WaitTimeoutError,
|
|
310
|
+
ActionError,
|
|
311
|
+
DriverCreationError,
|
|
312
|
+
DeviceInfo,
|
|
313
|
+
DriverConfig,
|
|
314
|
+
MobileActions,
|
|
315
|
+
Waiter,
|
|
316
|
+
build_driver_config,
|
|
317
|
+
create_driver,
|
|
318
|
+
load_settings,
|
|
319
|
+
apply_cli_overrides,
|
|
320
|
+
)
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Stable public modules (direct import):
|
|
324
|
+
- `appium_pytest_kit.settings`
|
|
325
|
+
- `appium_pytest_kit.driver`
|
|
326
|
+
- `appium_pytest_kit.waits`
|
|
327
|
+
- `appium_pytest_kit.actions`
|
|
328
|
+
- `appium_pytest_kit.errors`
|
|
329
|
+
- `appium_pytest_kit.interfaces` — `CapabilitiesAdapter` protocol for custom adapters
|
|
330
|
+
|
|
331
|
+
Private/internal modules (no compatibility guarantee):
|
|
332
|
+
- `appium_pytest_kit._internal.*`
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
## Fixture lifecycle
|
|
337
|
+
|
|
338
|
+
```mermaid
|
|
339
|
+
flowchart TD
|
|
340
|
+
A["pytest start"] --> B["load defaults + .env + env vars"]
|
|
341
|
+
B --> C["apply --app-* CLI overrides"]
|
|
342
|
+
C --> D["settings fixture (session)"]
|
|
343
|
+
D --> E{"APP_MANAGE_APPIUM_SERVER"}
|
|
344
|
+
E -->|"true"| F["start local Appium server"]
|
|
345
|
+
E -->|"false"| G["use APP_APPIUM_URL"]
|
|
346
|
+
F --> H["appium_server fixture"]
|
|
347
|
+
G --> H
|
|
348
|
+
H --> I{"session_mode"}
|
|
349
|
+
I -->|"clean-session / debug"| J["_driver_shared (session)"]
|
|
350
|
+
I -->|"clean"| K["driver per test"]
|
|
351
|
+
J --> K
|
|
352
|
+
K --> L["waiter / actions fixtures"]
|
|
353
|
+
K --> M["test executes"]
|
|
354
|
+
M --> N{"failed?"}
|
|
355
|
+
N -->|"yes"| O["capture screenshot + page source"]
|
|
356
|
+
N --> P["video stop (per policy)"]
|
|
357
|
+
O --> P
|
|
358
|
+
P --> Q["driver.quit() if clean mode"]
|
|
359
|
+
Q --> R["optional report summary flush"]
|
|
360
|
+
R --> S["optional server stop"]
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
## Local development
|
|
366
|
+
|
|
367
|
+
```bash
|
|
368
|
+
pip install -e ".[dev]"
|
|
369
|
+
ruff check .
|
|
370
|
+
pytest -q
|
|
371
|
+
pytest --collect-only examples/basic/tests -q
|
|
372
|
+
```
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
# appium-pytest-kit
|
|
2
|
+
|
|
3
|
+
`appium-pytest-kit` is a reusable Appium 2.x + pytest framework library for Python 3.11+.
|
|
4
|
+
|
|
5
|
+
- `pip install appium-pytest-kit` (or install from GitHub — see below)
|
|
6
|
+
- `appium-pytest-kit-init` to bootstrap configuration, or `--framework` to scaffold a full project
|
|
7
|
+
- Write tests immediately with built-in fixtures and zero boilerplate
|
|
8
|
+
|
|
9
|
+
**Full documentation:** [DOCUMENTATION.md](./DOCUMENTATION.md)
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
### From PyPI (once published)
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install appium-pytest-kit
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### From GitHub
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# latest main branch
|
|
25
|
+
pip install git+https://github.com/gianlucasoare/appium-pytest-kit.git
|
|
26
|
+
|
|
27
|
+
# specific branch
|
|
28
|
+
pip install git+https://github.com/gianlucasoare/appium-pytest-kit.git@main
|
|
29
|
+
|
|
30
|
+
# specific tag
|
|
31
|
+
pip install git+https://github.com/gianlucasoare/appium-pytest-kit.git@v0.1.0
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Local clone (editable, for development)
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
git clone https://github.com/gianlucasoare/appium-pytest-kit.git
|
|
38
|
+
cd appium-pytest-kit
|
|
39
|
+
pip install -e ".[dev]"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Quickstart
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
python -m venv .venv
|
|
48
|
+
source .venv/bin/activate
|
|
49
|
+
pip install git+https://github.com/gianlucasoare/appium-pytest-kit.git
|
|
50
|
+
appium-pytest-kit-init # creates .env with starter config
|
|
51
|
+
# or scaffold a full project:
|
|
52
|
+
appium-pytest-kit-init --framework --root my-project
|
|
53
|
+
pytest -q
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Edit `.env` with your device and app details, then write tests.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Step-by-step: test a real app in 5 minutes
|
|
61
|
+
|
|
62
|
+
This example tests the Android Calculator on an emulator. See [DOCUMENTATION.md](./DOCUMENTATION.md) for the full iOS walkthrough and all options.
|
|
63
|
+
|
|
64
|
+
### 1. Start Appium and an emulator
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
appium &
|
|
68
|
+
emulator -avd Pixel_7_API_33 &
|
|
69
|
+
adb devices # confirm emulator-5554 is listed
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 2. Configure `.env`
|
|
73
|
+
|
|
74
|
+
```env
|
|
75
|
+
APP_PLATFORM=android
|
|
76
|
+
APP_APPIUM_URL=http://127.0.0.1:4723
|
|
77
|
+
APP_DEVICE_NAME=emulator-5554
|
|
78
|
+
APP_PLATFORM_VERSION=13
|
|
79
|
+
APP_APP_PACKAGE=com.google.android.calculator
|
|
80
|
+
APP_APP_ACTIVITY=com.android.calculator2.Calculator
|
|
81
|
+
APP_NO_RESET=true
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 3. Write a test
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
# tests/test_calculator.py
|
|
88
|
+
import pytest
|
|
89
|
+
from appium.webdriver.common.appiumby import AppiumBy
|
|
90
|
+
|
|
91
|
+
BTN_2 = (AppiumBy.ACCESSIBILITY_ID, "2")
|
|
92
|
+
BTN_PLUS = (AppiumBy.ACCESSIBILITY_ID, "plus")
|
|
93
|
+
BTN_3 = (AppiumBy.ACCESSIBILITY_ID, "3")
|
|
94
|
+
BTN_EQUALS = (AppiumBy.ACCESSIBILITY_ID, "equals")
|
|
95
|
+
RESULT = (AppiumBy.RESOURCE_ID, "com.google.android.calculator:id/result_final")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@pytest.mark.integration
|
|
99
|
+
def test_addition(actions):
|
|
100
|
+
actions.tap(BTN_2)
|
|
101
|
+
actions.tap(BTN_PLUS)
|
|
102
|
+
actions.tap(BTN_3)
|
|
103
|
+
actions.tap(BTN_EQUALS)
|
|
104
|
+
assert actions.text(RESULT) == "5"
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### 4. Run it
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
pytest -m integration -v
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Built-in fixtures
|
|
116
|
+
|
|
117
|
+
| Fixture | Scope | Description |
|
|
118
|
+
|---|---|---|
|
|
119
|
+
| `settings` | session | Resolved `AppiumPytestKitSettings` — access any config field |
|
|
120
|
+
| `appium_server` | session | Server URL and whether it is framework-managed |
|
|
121
|
+
| `driver` | function | Live `appium.webdriver.Remote`, quit automatically after each test |
|
|
122
|
+
| `waiter` | function | Explicit waits with `WaitTimeoutError` on timeout |
|
|
123
|
+
| `actions` | function | High-level UI helpers: `tap`, `type_text`, `text`, `exists`, `swipe`, and more |
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Session modes
|
|
128
|
+
|
|
129
|
+
Control driver lifecycle per test or across the whole session:
|
|
130
|
+
|
|
131
|
+
```env
|
|
132
|
+
APP_SESSION_MODE=clean # default: fresh driver per test
|
|
133
|
+
APP_SESSION_MODE=clean-session # shared driver, app reset between tests
|
|
134
|
+
APP_SESSION_MODE=debug # shared driver, no reset (fast local debugging)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Device resolution (3-tier priority)
|
|
140
|
+
|
|
141
|
+
1. **Explicit** — `APP_DEVICE_NAME` / `APP_UDID` set in `.env` or CLI
|
|
142
|
+
2. **Profile** — `APP_DEVICE_PROFILE=pixel7` resolved from `data/devices.yaml`
|
|
143
|
+
3. **Auto-detect** — `adb devices` (Android) or `xcrun simctl` / `xctrace` (iOS)
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
# Use a named profile from data/devices.yaml
|
|
147
|
+
pytest --app-device-profile pixel7
|
|
148
|
+
|
|
149
|
+
# Auto-detect (no device settings needed)
|
|
150
|
+
pytest
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## Failure diagnostics
|
|
156
|
+
|
|
157
|
+
On test failure the framework automatically captures:
|
|
158
|
+
- **Screenshot** → `artifacts/screenshots/<test_id>.png`
|
|
159
|
+
- **Page source** → `artifacts/pagesource/<test_id>.xml`
|
|
160
|
+
- **Video** (if policy allows) → `artifacts/videos/<test_id>.mp4`
|
|
161
|
+
|
|
162
|
+
```env
|
|
163
|
+
APP_VIDEO_POLICY=never # default
|
|
164
|
+
APP_VIDEO_POLICY=failed # record and save only on failure
|
|
165
|
+
APP_VIDEO_POLICY=always # record every test
|
|
166
|
+
APP_ARTIFACTS_DIR=artifacts
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Allure attachments are added automatically when `allure-pytest` is installed.
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Configuration
|
|
174
|
+
|
|
175
|
+
Settings are loaded from `.env` → environment variables → CLI flags (highest wins).
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
pytest --app-platform ios
|
|
179
|
+
pytest --app-device-name "Pixel 7" --app-platform-version 13
|
|
180
|
+
pytest --appium-url http://192.168.1.10:4723
|
|
181
|
+
pytest --app-app-package com.example.app --app-app-activity .MainActivity
|
|
182
|
+
pytest --app-session-mode clean-session
|
|
183
|
+
pytest --app-device-profile pixel7
|
|
184
|
+
pytest --app-video-policy failed
|
|
185
|
+
pytest --app-is-simulator
|
|
186
|
+
pytest --app-capabilities-json '{"autoGrantPermissions": true}'
|
|
187
|
+
pytest --app-manage-appium-server # start Appium automatically
|
|
188
|
+
pytest --app-reporting-enabled # write artifacts/appium-pytest-kit/summary.json
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
See [DOCUMENTATION.md § Configuration](./DOCUMENTATION.md#5-configuration) for the full settings table.
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Expanded waits
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
waiter.for_clickable(locator) # wait for element to be tappable
|
|
199
|
+
waiter.for_invisibility(locator) # wait for element to disappear
|
|
200
|
+
waiter.for_text_contains(locator, "partial text") # wait for text substring
|
|
201
|
+
waiter.for_text_equals(locator, "exact text") # wait for exact text match
|
|
202
|
+
waiter.for_all_visible([loc1, loc2, loc3]) # wait for all elements to appear
|
|
203
|
+
waiter.for_any_visible([loc1, loc2]) # wait for first visible element
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Expanded actions
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
actions.tap_if_present(locator) # tap only if visible, returns bool
|
|
212
|
+
actions.clear(locator) # clear a text field
|
|
213
|
+
actions.swipe(sx, sy, ex, ey) # W3C Pointer swipe gesture
|
|
214
|
+
actions.scroll_down() # swipe up on screen center
|
|
215
|
+
actions.scroll_up() # swipe down on screen center
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Extension hooks
|
|
221
|
+
|
|
222
|
+
Implement these in your `conftest.py` to customise behaviour without touching the framework:
|
|
223
|
+
|
|
224
|
+
```python
|
|
225
|
+
# conftest.py
|
|
226
|
+
|
|
227
|
+
def pytest_appium_pytest_kit_capabilities(capabilities, settings):
|
|
228
|
+
"""Add extra capabilities before each driver session."""
|
|
229
|
+
return {"autoGrantPermissions": True, "language": "en"}
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def pytest_appium_pytest_kit_configure_settings(settings):
|
|
233
|
+
"""Replace settings at session start."""
|
|
234
|
+
return settings.model_copy(update={"implicit_wait": 2.0})
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def pytest_appium_pytest_kit_driver_created(driver, settings):
|
|
238
|
+
"""Run setup immediately after each driver is created."""
|
|
239
|
+
driver.orientation = "PORTRAIT"
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## Project scaffolding
|
|
245
|
+
|
|
246
|
+
Generate a full project structure in one command:
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
appium-pytest-kit-init --framework --root my-project
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Creates:
|
|
253
|
+
```
|
|
254
|
+
my-project/
|
|
255
|
+
├── data/devices.yaml # device profiles
|
|
256
|
+
├── pages/
|
|
257
|
+
│ ├── base_page.py # BasePage composition class
|
|
258
|
+
│ └── example_page.py # starter page object
|
|
259
|
+
├── flows/ # reusable multi-step flows
|
|
260
|
+
├── tests/
|
|
261
|
+
│ ├── android/test_smoke.py
|
|
262
|
+
│ └── ios/test_smoke.py
|
|
263
|
+
├── conftest.py
|
|
264
|
+
├── pytest.ini
|
|
265
|
+
└── .env.example
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## Public API
|
|
271
|
+
|
|
272
|
+
Top-level imports (stable):
|
|
273
|
+
|
|
274
|
+
```python
|
|
275
|
+
from appium_pytest_kit import (
|
|
276
|
+
AppiumPytestKitSettings,
|
|
277
|
+
AppiumPytestKitError,
|
|
278
|
+
ConfigurationError,
|
|
279
|
+
DeviceResolutionError,
|
|
280
|
+
LaunchValidationError,
|
|
281
|
+
WaitTimeoutError,
|
|
282
|
+
ActionError,
|
|
283
|
+
DriverCreationError,
|
|
284
|
+
DeviceInfo,
|
|
285
|
+
DriverConfig,
|
|
286
|
+
MobileActions,
|
|
287
|
+
Waiter,
|
|
288
|
+
build_driver_config,
|
|
289
|
+
create_driver,
|
|
290
|
+
load_settings,
|
|
291
|
+
apply_cli_overrides,
|
|
292
|
+
)
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Stable public modules (direct import):
|
|
296
|
+
- `appium_pytest_kit.settings`
|
|
297
|
+
- `appium_pytest_kit.driver`
|
|
298
|
+
- `appium_pytest_kit.waits`
|
|
299
|
+
- `appium_pytest_kit.actions`
|
|
300
|
+
- `appium_pytest_kit.errors`
|
|
301
|
+
- `appium_pytest_kit.interfaces` — `CapabilitiesAdapter` protocol for custom adapters
|
|
302
|
+
|
|
303
|
+
Private/internal modules (no compatibility guarantee):
|
|
304
|
+
- `appium_pytest_kit._internal.*`
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
## Fixture lifecycle
|
|
309
|
+
|
|
310
|
+
```mermaid
|
|
311
|
+
flowchart TD
|
|
312
|
+
A["pytest start"] --> B["load defaults + .env + env vars"]
|
|
313
|
+
B --> C["apply --app-* CLI overrides"]
|
|
314
|
+
C --> D["settings fixture (session)"]
|
|
315
|
+
D --> E{"APP_MANAGE_APPIUM_SERVER"}
|
|
316
|
+
E -->|"true"| F["start local Appium server"]
|
|
317
|
+
E -->|"false"| G["use APP_APPIUM_URL"]
|
|
318
|
+
F --> H["appium_server fixture"]
|
|
319
|
+
G --> H
|
|
320
|
+
H --> I{"session_mode"}
|
|
321
|
+
I -->|"clean-session / debug"| J["_driver_shared (session)"]
|
|
322
|
+
I -->|"clean"| K["driver per test"]
|
|
323
|
+
J --> K
|
|
324
|
+
K --> L["waiter / actions fixtures"]
|
|
325
|
+
K --> M["test executes"]
|
|
326
|
+
M --> N{"failed?"}
|
|
327
|
+
N -->|"yes"| O["capture screenshot + page source"]
|
|
328
|
+
N --> P["video stop (per policy)"]
|
|
329
|
+
O --> P
|
|
330
|
+
P --> Q["driver.quit() if clean mode"]
|
|
331
|
+
Q --> R["optional report summary flush"]
|
|
332
|
+
R --> S["optional server stop"]
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## Local development
|
|
338
|
+
|
|
339
|
+
```bash
|
|
340
|
+
pip install -e ".[dev]"
|
|
341
|
+
ruff check .
|
|
342
|
+
pytest -q
|
|
343
|
+
pytest --collect-only examples/basic/tests -q
|
|
344
|
+
```
|