Fastbot Android
以下流程皆是參考CSDN-Android APP穩定性測試工具Fastbot和官方中文教學
一般使用
- 把repo clone下來,並且把一些檔案複製到手機
1
2
3
4
5
6$ git clone https://github.com/bytedance/Fastbot_Android.git $ cd Fastbot_Android $ adb push fastbot-thirdpart.jar /sdcard $ adb push framework.jar /sdcard $ adb push monkeyq.jar /sdcard $ adb push libs/. /data/local/tmp/
- dump apk內部會使用到的strings,並且複製到手機
1
2$ aapt2 dump strings <your apk name> > max.valid.strings $ adb push max.valid.strings /sdcard
- 獲取device number和package name ```bash $ adb devices List of devices attached 24121JEGR04513 device $ aapt2 dump badging “Spotify_ Music and Podcasts_8.9.60.560_APKPure.apk” | findstr “package” package: name=’com.spotify.music’ versionCode=’116920144’ versionName=’8.9.60.560’ platformBuildVersionName=’14’ platformBuildVersionCode=’34’ compileSdkVersion=’34’ compileSdkVersionCodename=’14’ uses-permission: name=’com.sec.android.app.clockpackage.permission.READ_ALARM’
- 實際測試
1
2
3$ adb shell CLASSPATH=/sdcard/monkeyq.jar:/sdcard/framework.jar:/sdcard/fastbot-thirdpart.jar exec app_process /system/bin com.android.commands.monkey.Monkey -p <package name> --agent reuseq --running-minutes <遍歷時長> --throttle <事件頻率> -v -v --- $ adb shell CLASSPATH=/sdcard/monkeyq.jar:/sdcard/framework.jar:/sdcard/fastbot-thirdpart.jar exec app_process /system/bin com.android.commands.monkey.Monkey -p com.spotify.music --agent reuseq --running-minutes 1 --throttle 500 -v -v --output-directory /sdcard/fastbot_results&adb pull /sdcard/fastbot_results D:\Downloads
輸入自訂Strings
- Download ADBKeyBoard,安裝後設定預設keyboard為ADBKeyboard
1
2
3
4$ wget https://github.com/senzhk/ADBKeyBoard/raw/master/ADBKeyboard.apk $ adb install ADBKeyboard.apk $ adb shell ime enable com.android.adbkeyboard/.AdbIME $ adb shell ime set com.android.adbkeyboard/.AdbIME
- 設定config並push到手機
1
2$ echo "max.randomPickFromStringList = true" > max.config $ adb push max.config /sdcard
- 設定像要輸入的strings並push到手機
1
2$ echo "test string" > max.strings $ adb push max.strings /sdcard
:::info
- 如何設定成原本的keyboard
1
$ adb shell ime reset
- 傳送text
1
$ adb shell am broadcast -a ADB_INPUT_TEXT --es msg 'test'
- 全選目前textview的strings
1
$ adb shell am broadcast -a ADB_INPUT_TEXT --es mcode '4096,29'
- Delete Strings
1
$ adb shell am broadcast -a ADB_INPUT_CODE --ei code 67
:::
- 如何設定成原本的keyboard
自訂前期的Script
如果想要自行設定前期的登入或是註冊這樣的flow,就可以利用這個模式,只要先設定好XPATH和action,Fastbot就會按照我們給定的config去執行,執行完了之後就會繼續執行我們前面給的command依序crawl
設定config
這個部分有點複雜,如果是像spotify這樣因為無法screenshot而無法使用Appium Inspector工具的就會更複雜
- 在PC上創一個
max.xpath.actions
文件 - 利用Maxim知道目前的activity name
1
2
3
4
5
6
7$ git clone https://github.com/zhangzhao4444/Maxim.git $ cd Maxim $ adb push framework.jar /sdcard $ adb push monkey.jar /sdcard $ adb shell CLASSPATH=/sdcard/Maxim/monkey.jar:/sdcard/Maxim/framework.jar exec app_process /system/bin tv.panda.test.monkey.api.CurrentActivity [Maxim] current activity: [Maxim] // com.spotify.login.loginflowimpl.LoginActivity
- 獲取想要互動的View的XPATH
如果可以screenshot的話,就可以利用Appium-Inspector,否則可以用我給的script慢慢爬,只要手機USB連線,並且開啟想要爬的activity頁面,執行下方script,就會print出目前和該app有關的clickable/editable view XPATH,以下用spotify為例
:::spoiler Fetch XPATH Script
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
94import os import uiautomator2 as u2 import xml.etree.ElementTree as ET def adb_devices() -> str: # 取得所有模擬器名稱與狀態 cmd = 'adb devices' process = os.popen(cmd) result = process.read().split('\n') text = [] for line in result: if 'List of devices' not in line and line != '' and line != ' ': # 去掉標題跟空白字串 text.append(line) devices = {} for i in range(len(text)): words = text[i].split('\t') # 格式: emulator-5554\tdevice if 'device' in words[1]: # 只存有啟動的模擬器 devices[i] = words[0] # 用模擬器index當key if len(devices) == 0: print('[x] None emulator is running') raise Exception return devices def connect_single_device(emulator: str) -> u2.Device: try: d = u2.connect(emulator) return d except: print('[x] Unable to connect to the emulator') raise Exception def generate_xpath(view): """ 根据视图的属性生成 XPath。 :param view: 视图的属性字典 :return: 生成的 XPath 字符串 """ xpath = f"//{view['class']}" conditions = [] if view['index']: conditions.append(f"@index='{view['index']}'") if view['text']: conditions.append(f"@text='{view['text']}'") if view['resource-id']: conditions.append(f"@resource-id='{view['resource-id']}'") if view['package']: conditions.append(f"@package='{view['package']}'") if view['content-desc']: conditions.append(f"@content-desc='{view['content-desc']}'") if view['clickable']: conditions.append(f"@clickable='{view['clickable']}'") if conditions: xpath += "[" + " and ".join(conditions) + "]" return xpath def main(emulator: str): d = connect_single_device(emulator) # get the UI hierarchy dump content xml = d.dump_hierarchy(compressed=False, pretty=True, max_depth=50) root = ET.fromstring(xml) if clickable_views.get(activity_level) is None: clickable_views[activity_level] = [] if editable_views.get(activity_level) is None: editable_views[activity_level] = [] # Find all clickable & editable views for view in root.iter(): resource_id = view.attrib.get('resource-id', '') clickable = view.attrib.get('clickable', 'false') package_name = view.attrib.get('package', '') if any(app_name.lower() in a for a in [resource_id, package_name]) and clickable == 'true': print("Clickable View XPATH: ", generate_xpath(view.attrib)) for view in root.findall(".//*[@class='android.widget.EditText']"): resource_id = view.attrib.get('resource-id', '') if app_name.lower() in resource_id: print("Editable View XPATH: ", generate_xpath(view.attrib)) if __name__ == '__main__': clickable_views = {} editable_views = {} app_name = 'Spotify' activity_level = 0 try: emulators = adb_devices() except: print('[x] Cannot get the list of emulators, the program will be terminated. Please use adb devices to check if the emulator is running') exit() main(emulators[0])
:::
1
2
3
4
5$ python fetchXPATH.py Clickable View XPATH: //android.widget.Button[@index='0' and @text='Sign up free' and @package='com.spotify.music' and @clickable='true'] Clickable View XPATH: //android.widget.Button[@index='1' and @text='Continue with Google' and @package='com.spotify.music' and @clickable='true'] Clickable View XPATH: //android.widget.Button[@index='2' and @text='Continue with Facebook' and @package='com.spotify.music' and @clickable='true'] Clickable View XPATH: //android.widget.Button[@index='3' and @text='Log in' and @package='com.spotify.music' and @clickable='true']
- 接下來就把XPATH貼到下方格式的地方就可以了
在同一個activity的步驟不需要拆分,詳細說明可以看Fastbot handbook
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[ { "prob": 1, "activity": "com.spotify.login.loginflowimpl.LoginActivity", "times": 1, "actions": [ { "xpath": "//android.widget.Button[@index='3' and @text='Log in' and @package='com.spotify.music' and @clickable='true'", "action": "CLICK", "throttle" : 2000 }, { "xpath": "//*[@resource-id='com.spotify.music:id/username_text']", "action": "CLICK", "text": "username", "throttle" : 2000 }, { "xpath": "//*[@resource-id='com.spotify.music:id/password_text']", "action": "CLICK", "text": "password", "throttle" : 2000 }, { "xpath": "//*[@resource-id='com.spotify.music:id/login_button' and @text='Log in']", "action": "CLICK", "throttle" : 2000 } ] } ]
把以上json格式填寫好後,丟到Json Checker檢查有無問題,注意最一開始和最後一定是中括號,格式正確後再填寫到==max.xpath.actions==,並且丟到手機上
1
$ adb push max.xpath.actions /sdcard
注意
:::info
- 如果有想要填寫文字的情況就一定要保證是用ADBKeyboard的狀態下才會如實的填寫我們設定好的text
- Fastbot預設會到/sdcard抓max.xpath.actions,所以不要放到其他地方 :::