-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathAppium(Selenium)-Mobile Automation Testing from Scratch=Rahul Shetty;Note=Erxin.txt
715 lines (511 loc) · 19.9 KB
/
Appium(Selenium)-Mobile Automation Testing from Scratch=Rahul Shetty;Note=Erxin.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
Appium(Selenium)-Mobile Automation Testing from Scratch=Rahul Shetty;Note=Erxin
# Appium introduction
- cross device UI testing tool
# Step by step to setup appium on window
1. download java and set Java_Home environment
download JDK from oracle, jdk-8u121-windows-x64.exe etc.
add Java_Home
add Java jdk bin to Path environment variable
2. download Android STUDIO and install
https://developer.andriod.com/studio/index.html
3. set Android_Home environment to SDK location
4. open Android Studio and setup emulator 2.3 in the video. the latest is 4.2 now!
add android sdk bin path to Path environment variable
add android sdk tool bin path to Path environment variable
add android sdk plantform-tools to Path environment variable
open android studio and open AVD manager to install the emulator
create virtual device
+ open emulator from command line
$ <android sdk>\platform-tools\emulator -avd <DeviceName>
5. Download Node.js
https://nodejs.org/en/download/
6. set Node_Home environment
7. download Appium Server
$ npm install -g appium
$ appium
to start the server
8. Download Appium Java client library
https://search.maven.org/
g:io appium a:java-client
9. Install Eclipse
10. Start Appium server
# java document
- reference
https://docs.oracle.com/en/java/javase/16/index.html
# Official document
## API
- reference
https://github.com/appium/appium/blob/master/docs/en/about-appium/api.md
- commands
https://github.com/appium/appium/tree/master/docs/en/commands
## advanced concepts
- element finding plugins
+ reference
https://github.com/appium/appium/blob/master/docs/en/advanced-concepts/element-finding-plugins.md
+ install this plugin wherever you like on your system, though there are three basic options:
A directory you manage separately from Appium (by running npm install <plugin> in an arbitrary folder)
Inside the Appium dependency tree itself (by running npm install <plugin> inside the Appium root directory)
Globally on your system (by running npm install -g <plugin>)
+ add a new capability to your test: customFindModules. This capability must be an object with at least one key and one value.
```
{
"customFindModules": {
"plug": "my-element-finding-plugin"
}
}
```
when start a session with this capability. we can find a element using a registered plugin
```
drive.findElement('-custom', 'plug:foo');
```
if there is only one plugin then the plug: can be omitted
+ develop a plugin
must be a node module
must return a array of element objects
when appium calls your find method, it will pass the following parameters
an instance of the driver representing the session (ex. XCUITestDriver)
a logging object
the selector
a boolean value, whether the user is looking for mutiple elements
+ known plugins
https://github.com/testdotai/appium-classifier-plugin
https://www.npmjs.com/package/test-ai-classifier
- event timing, retrive timing information about startup and command length
GET /session/:id
```
{
"<event_type>": [<occurence_timestamp_1>, ...],
"commands": [
{
"cmd": "<command_name>",
"startTime": <js_timestamp>,
"endTime": <js_timestamp>
},
...
]
}
```
+add custom event
You can send a custom event name to the Appium server using the Log Event API
- selenium grid, using selenium grid (setup docs)
```
> appium --nodeconfig /path/to/nodeconfig.json
# or, if running from source:
> node . --nodeconfig /path/to/nodeconfig.json
```
- image elements, experimental -image locator strategy, it is possible to send an Appium an image file representing an element you want to tap
driver.findElementByImage()
+ image selector a base64-encoded image file representing the template you want to use for matching
+ support small number of methods, as if it were a bona-fide WebElement
click
isDisplayed
getSize
getLocation
getLocationInView
getElementRect
getAttribute
* visual, returns matched image as base64 data
* score, return the similarity score
+ debug
```
# Ruby core
@driver.update_settings({ getMatchedImageResult: true })
el = @driver.find_element_by_image 'path/to/img.ong'
img_el.visual # returns base64 encoded string
# Python
self.driver.update_settings({"getMatchedImageResult": True})
el = self.driver.find_element_by_image('path/to/img.ong')
el.get_attribute('visual') # returns base64 encoded string
```
- log filters, Appium supports --log-filters command line argument. The filtering config must be a valid JSON file containing array of filtering rules. fields of the rule
pattern, regexp
text, non-empty exact text match
flags, regular expression flags
replacer, the replacer value to use, by default equals to **SECURE**
+ example
[
{
"pattern": "my\\.magic\\.\\w",
"flags": "i",
"replacer": "***"
}
]
- memory collection, executes the appium.js, collect the dumps of Appium's memory usage to be analyzed for problems
```
--heapsnapshot-signal=<signal>
```
- migrating your ios tests from UIAutomation(iOS 9.3 and below) to XCUITest (iOS 9.3 and up )
- running appium with multiple xcode versions installed
xcode-select tool
$ sudo xcode-select -s /Applications/Xcode7.app/Contents/Developer
$ appium
set environment
$ export DEVELOPER_DIR=/Applications/Xcode9.app/Contents/Developer
- parallel android tests
+ Appium provides a way for users to automate multiple Android sessions on a single machine on single server instance
+ important capabilities
udid, the device id
chromedriverPort
mjpegServerPort
systemPort
+ parallel iOS tests
* RealDevice
udid
wdaLocalPort
derivedDataPath
* Simulator
udid
deviceName
platformVersion
wdaLocalPort
derivedDataPath
- settings, Settings让appium忽略当前不可见的元素
可变性,Settings在一个会话中是可以被修改的。
临时性,Settings只对当前会话生效,新建立的会话会被重置。
局限性,Settings只用来控制appium server,不能用于控制被测应用或设备。
POST /session/:sessionId/appium/settings
- WebDriverAgent Server
Appium backend is using Facebook's WebDriverAgent
$ cd /usr/local/lib/node_modules/appium/node_modules/appium-xcuitest-driver/WebDriverAgent
$ ./Scripts/bootstrap.sh -d
...
# Running from source
- node
highly recommended.
NVM - https://github.com/creationix/nvm node version manager
N - https://github.com/tj/n interactive manage node version
- hacking on appium
```
npm install -g appium-doctor
appium-doctor --dev
npm install
```
Install the appium-doctor tool, and run it to verify all of the dependencies are set up correctly (since dependencies for building Appium are different from those for simply running
```
npm install -g appium-doctor
appium-docker --dev
npm install
# remove old dependencies
rm -rf node_modules && rm -rf package-lock.json && npm install
# start appium server
node .
```
- appium docker commandline
```
appium-doctor -h
Usage: appium-doctor.js [options, defaults: --ios --android]
Options:
--ios Check iOS setup [boolean]
--android Check Android setup [boolean]
--dev Check dev setup [boolean]
--debug Show debug messages [boolean]
--yes Always respond yes [boolean]
--no Always respond no [boolean]
--demo Run appium-doctor demo (for dev). [boolean]
-h, --help Show help [boolean]
```
remove old dependencies, pulling new code
$ rm -rf node_modules && rm -rf package-lock.json && npm install
- start android emulator
$ emulator -avd <MyAvdName>
- run test
$ npm run test
all e2e test
$ npm run e2e-test
- different packages
link two npm packages
package A depends on package B. link the two
```
cd B
# create symbolic link
npm link
cd A
# use npm link to link the dependent package B
npm link B
```
npm link in a package folder will create a symlink in the global folder {prefix}/lib/node_modules/<package> that links to the package where the npm link command was executed
npm link package-name will create a symbolic link from globally-installed package-name to node_modules/ of the current folder
- debugging node
+ run debug model, run appium server in debug mode and set breakpoint in VSCode
+ attach debug
Attach Debug: Attach to a currently running Appium server
Example Usage
From root, run node --inspect-brk . --port 5555
Run attach debug
Setup breakpoints in VSCode
# Appium packages
- appium packages
+ core packages
appium
appium-express
appium-base-driver
appium-docker
+ utility packages
appium-logger
appium-support
appium-gulp-plugins
node-teen_process
+ android packages
appium-android driver
appoium-adb
aapium-android-bootstrap
appium-uiautomator
appoium-selendroid-driver
appiumselendroid-installer
appoium-android-ime
+ selenium packages
...
+ ios packages
...
- appium-base-driver
designed to have a single testing session per instantiation
contains constraints on caps (platformName has to be present, etc)
validates capabilities
runs chain of promised commands with single concurrency
handles session restart
handles swipe options
exports class (DeviceSettings) to manage device settings (get/update)
contains basic commands
to find elements
create/delete sessions
handle timeouts
set/update device settings
provides helper methods for commands
- appium-adb
wrapper around the Android Debug Bridge (adb)
contains a bunch of commands that are basically just rpc to the adb binary
houses jar files to run for special use cases like signing, verifying apps or moving manifests
allows special (mobile specific) emulator commands that are not related to the webdriver protocol like
locking the screen
press back button
press home button
set/get airplane mode
set/get wifi state
captures logcat
handles emulator/simulator actions (e.g. reboot)
- appium-remote-debugger
RPC client to connect Appium to iOS webviews
can connect to WebKit devtools
for iOS only
has two rpc client classes
remote-debugger-rpc-client: uses tcp6 that connects to localhost:27753
webkit-rpc-client: uses WebSockets to connect to ws://localhost:27753/devtools/page/${pageId}
- appium-express (part of appium-base-driver)
starts express server (allows x-domain-origin)
initialises routes from AppiumDriver
timeout handling
serves a static page for test purposes
connects req/res events to logger
- development command
```
npm run build Transpile code into the build directory
npm run lint Runs ESLint
npm run test Cleans, lints, transpiles, and runs unit tests
npm run e2e-test Tranpiles and runs functional tests
npm run watch Automatically runs test command when code is changed
npm run mocha Gives access to mocha test runner
```
- developer overview
https://github.com/appium/appium/blob/master/docs/en/contributing-to-appium/developers-overview.md
- real device continuous integration
Appium uses a service called TestObject that allows us to run automated functional tests against real devices.
you need to set some environment variables
AWS_ACCESS_KEY_ID: ID with S3 write access
AWS_SECRET_ACCESS_KEY
AWS_REGION
AWS_S3_BUCKET: Name of the bucket being written to
TESTOBJECT_API_KEY
- release doc
...
- style guide
...
- version branch release
...
# drivers
- android uiautomator2
https://github.com/appium/appium/blob/master/docs/en/drivers/android-uiautomator2.md
...
# writing running appium test
- android
https://github.com/appium/appium/tree/master/docs/en/writing-running-appium/android
# appium client libraries
- reference
http://appium.io/downloads
- list of client libraries
+ appium repository
Ruby https://github.com/appium/ruby_lib, https://github.com/appium/ruby_lib_core
Python https://github.com/appium/python-client
Java https://github.com/appium/java-client
C# (.NET) https://github.com/appium/appium-dotnet-driver
+ community
JavaScript (Node.js) https://github.com/admc/wd
JavaScript (Node.js) https://github.com/webdriverio/webdriverio
JavaScript (Browser) https://github.com/projectxyzio/web2driver
RobotFramework https://github.com/serhatbolsu/robotframework-appiumlibrary
# Espresso Driver for android
- reference
https://appium.io/docs/en/drivers/android-espresso/index.html
- install steps
Windows, Linux and macOS are supported as hosts
Android SDK Platform tools must be installed. Android Studio IDE also provides a convenient UI to install and manage the tools.
ANDROID_HOME or ANDROID_SDK_ROOT environment variable must be set
Java JDK must be installed and JAVA_HOME environment variable must be set. Android SDK below API 30 requires Java 8. Android SDK 30 and above requires Java 9 or newer.
Emulator platform image must be installed if you plan to run your tests on it. Android Studio IDE also provides a convenient UI to install and manage emulators.
Real Android devices must have USB debugging enabled and should be visible as online in adb devices -l output.
The minimum version of Android API must be 5.0 (API level 21) (6.0 is recommended as version 5 has some known compatibility issues).
Gradle must be installed in order to build Espresso server.
+ download gradle
+ copy gradle to c:\gradle
+ add bin to path environment variable
Both the server package and the application under test must be signed with the same digital signature. Appium does sign them automatically upon session creation, so this could only be an issue if one wants to test an application, which is already installed on the device (using noReset=true capability).
The package under test must not have mangled class names (e.g. Proguard must not be enabled for it)
# Appium document
- concepts
Automation is always performed in the context of a session. Clients initiate a session with a server in ways specific to each library, but they all end up sending a POST /session request to the server
client/server architecture, Appium is at its heart a webserver that exposes a REST API. It receives connections from a client, listens for commands, executes those commands on a mobile device,
Desired capabilities are a set of keys and values (i.e., a map or hash) sent to the Appium server to tell the server what kind of automation session we're interested in starting up
# Appium base driver
- reference
https://github.com/appium/appium-base-driver/tree/master/lib/basedriver
- Image element code
```
class ImageElement {
constructor(b64Template, rect, b64Result = null) {
this.template = b64Template;
this.rect = rect;
this.id = `${_protocol.IMAGE_ELEMENT_PREFIX}${_uuidJs.default.create().hex}`;
this.b64MatchedImage = b64Result;
}
get size() {
return {
width: this.rect.width,
height: this.rect.height
};
}
get location() {
return {
x: this.rect.x,
y: this.rect.y
};
}
get center() {
return {
x: this.rect.x + this.rect.width / 2,
y: this.rect.y + this.rect.height / 2
};
}
get matchedImage() {
return this.b64MatchedImage;
}
asElement(protocolKey) {
return {
[protocolKey]: this.id
};
}
equals(other) {
return this.rect.x === other.rect.x && this.rect.y === other.rect.y && this.rect.width === other.rect.width && this.rect.height === other.rect.height;
}
async click(driver) {
let newImgEl;
const {
autoUpdateImageElementPosition: updatePos,
checkForImageElementStaleness,
imageElementTapStrategy
} = driver.settings.getSettings();
if (!IMAGE_TAP_STRATEGIES.includes(imageElementTapStrategy)) {
throw new Error(`Incorrect imageElementTapStrategy setting ` + `'${imageElementTapStrategy}'. Must be one of ` + JSON.stringify(IMAGE_TAP_STRATEGIES));
}
if (checkForImageElementStaleness || updatePos) {
_logger.default.info('Checking image element for staleness before clicking');
try {
newImgEl = await driver.findByImage(this.template, {
shouldCheckStaleness: true,
ignoreDefaultImageTemplateScale: true
});
} catch (err) {
throw new _2.errors.StaleElementReferenceError();
}
if (!this.equals(newImgEl)) {
_logger.default.warn(`When trying to click on an image element, the image changed ` + `position from where it was originally found. It is now at ` + `${JSON.stringify(newImgEl.rect)} and was originally at ` + `${JSON.stringify(this.rect)}.`);
if (updatePos) {
_logger.default.warn('Click will proceed at new coordinates');
this.rect = _lodash.default.clone(newImgEl.rect);
} else {
_logger.default.warn('Click will take place at original coordinates. If you ' + 'would like Appium to automatically click the new ' + "coordinates, set the 'autoUpdateImageElementPosition' " + 'setting to true');
}
}
}
const {
x,
y
} = this.center;
_logger.default.info(`Will tap on image element at coordinate [${x}, ${y}]`);
if (imageElementTapStrategy === IMAGE_EL_TAP_STRATEGY_W3C) {
_logger.default.info('Will tap using W3C actions');
const action = {
type: 'pointer',
id: 'mouse',
parameters: {
pointerType: 'touch'
},
actions: [{
type: 'pointerMove',
x,
y,
duration: 0
}, {
type: 'pointerDown'
}, {
type: 'pause',
duration: TAP_DURATION_MS
}, {
type: 'pointerUp'
}]
};
if (driver.performActions) {
return await driver.performActions([action]);
}
_logger.default.warn('Driver does not seem to implement W3C actions, falling back ' + 'to TouchActions');
}
_logger.default.info('Will tap using MJSONWP TouchActions');
const action = {
action: 'tap',
options: {
x,
y
}
};
if (driver.performTouch) {
return await driver.performTouch([action]);
}
throw new Error("Driver did not implement the 'performTouch' command. " + 'For drivers to support finding image elements, they ' + "should support 'performTouch' and 'performActions'");
}
static async execute(driver, cmd, imgElId, ...args) {
if (!driver._imgElCache.has(imgElId)) {
throw new _2.errors.NoSuchElementError();
}
const imgEl = driver._imgElCache.get(imgElId);
switch (cmd) {
case 'click':
return await imgEl.click(driver);
case 'elementDisplayed':
return true;
case 'getSize':
return imgEl.size;
case 'getLocation':
case 'getLocationInView':
return imgEl.location;
case 'getElementRect':
return imgEl.rect;
case 'getAttribute':
if (args[0] === 'visual') {
return imgEl.matchedImage;
} else {
throw new _2.errors.NotYetImplementedError();
}
default:
throw new _2.errors.NotYetImplementedError();
}
}
}
```