多台设备并行运行monkey

比如有10 台设备,要在这10台设备上运行monkey 脚本们可以通过命令行的方式来进行。如果手动执行会比较麻烦;

运行单个手机 monkey

adb 命令

  1. adb shell monkey 500

monkey :https://developer.android.google.cn/studio/test/monkey?hl=zh-cn

subprocss 子进程方式执行命令

python 脚本, 执行一台设备的脚本

  1. import subprocess
  2. monkey_cmd = "adb -s 127.0.0.1:62001 shell monkey -p org.cnodejs.android.md -v --throttle 500 500"
  3. with open('./monkey_run.log',mode='w',encoding='utf8') as file:
  4. subprocess.Popen(monkey_cmd,stdout=file)

命令解释

  1. "adb -s 127.0.0.1:62001 shell monkey -p org.cnodejs.android.md -v --throttle 500 500"
  2. -s 127.0.0.1:62001 # 执行的手机设备串号
  3. -p org.cnodejs.android.md # 执行运行的app
  4. -v # 显示详细日志 日志级别分为三个基本 -vv 表示最详细的日志级别
  5. --throttle 500 # 每个事件之间的 间隔时间 为500 毫秒
  6. 500 # 执行500个事件

如果现在是两台设备

  1. C:\Users\zengy>adb devices
  2. List of devices attached
  3. 127.0.0.1:62001 device
  4. emulator-5554 device

写个for循环 启动子进程的方式运行多台设备

  1. import subprocess
  2. def run(device_id):
  3. monkey_cmd = f"adb -s {device_id} shell monkey -p org.cnodejs.android.md -v --throttle 500 500"
  4. with open('./monkey_run.log',mode='w',encoding='utf8') as file:
  5. subprocess.Popen(monkey_cmd,stdout=file,shell=True)
  6. if __name__ == '__main__':
  7. devices = ['127.0.0.1:62001',"emulator-5554"]
  8. for device in devices:
  9. run(device)

两台设备运行的时候都会把把日志写入到一个文件中。这样肯定是不行的
下面要求:

  1. 自动获取device的设备串号,比如设备连接 三台, 自动获取这三台手机的串号
  2. 自动执行monkey ,运行monkey的时候,每台设备产生的日志信息应该保存到对应的设备的日志中。日志文件名以设备串号命名。

使用代码的方式获取已经连接成功的设备

  1. C:\Users\zengy>adb devices
  2. List of devices attached
  3. emulator-5554 device
  4. emulator-5556 unauthorized

获取所有连接的设备

  1. import subprocess
  2. # 创建子进程
  3. devices = []
  4. proc = subprocess.Popen('adb devices',stdout=subprocess.PIPE,shell=True)
  5. for line in proc.stdout.readlines():
  6. # print(type(line),line)
  7. # 将字节类型转换为字符串
  8. linestr = line.decode(encoding='utf8')
  9. if '\tdevice' in linestr:
  10. # 字符串分割 提取 deviceid值
  11. device_id = linestr.strip().split('\tdevice')[0]
  12. devices.append(device_id)
  13. print(devices)

实现自动执行多台设备

  1. import subprocess
  2. def get_connect_devices():
  3. """
  4. 获取已经成功连接的设备
  5. :return: list
  6. """
  7. devices = []
  8. proc = subprocess.Popen('adb devices', stdout=subprocess.PIPE,shell=True)
  9. for line in proc.stdout.readlines():
  10. # print(type(line),line)
  11. # 将字节类型转换为字符串
  12. linestr = line.decode(encoding='utf8')
  13. if '\tdevice' in linestr:
  14. # 字符串分割 提取 deviceid值
  15. device_id = linestr.strip().split('\tdevice')[0]
  16. devices.append(device_id)
  17. return devices
  18. def run(device_id:str):
  19. # 字符串名字去掉特殊字符
  20. devicename = device_id.replace(':','_')
  21. devicefile = devicename.replace('.',"_")
  22. monkey_cmd = f"adb -s {device_id} shell monkey -p org.cnodejs.android.md -vv --throttle 500 500"
  23. with open(f'./{devicefile}.log',mode='w',encoding='utf8') as file:
  24. subprocess.Popen(monkey_cmd,stdout=file,shell=True)
  25. if __name__ == '__main__':
  26. devices = get_connect_devices()
  27. for device in devices:
  28. run(device)

multiprocessing 并行运行

通过多进程的方式执行自动化脚本
https://docs.python.org/zh-cn/3/library/multiprocessing.html

  1. import time
  2. def run():
  3. time.sleep(1)
  4. print('helloworld')
  5. time.sleep(1)
  6. print('end')
  7. if __name__ == '__main__':
  8. start= time.perf_counter()
  9. for i in range(4):
  10. run()
  11. end = time.perf_counter()
  12. print(f'执行用时: {end-start}s')

image.png

下面使用多进程的方式执行

  1. import time
  2. import multiprocessing
  3. import random
  4. def run(name):
  5. time.sleep(1)
  6. print(f'helloworld-{name}')
  7. time.sleep(1)
  8. print('end')
  9. if __name__ == '__main__':
  10. start= time.perf_counter()
  11. processes = []
  12. # 启动8个进程
  13. for i in range(8):
  14. p = multiprocessing.Process(target=run,args=(random.random(),)) # 注意参数
  15. processes.append(p)
  16. p.start() # 启动
  17. # 执行进程中的脚本
  18. for proce in processes:
  19. proce.join() # 加入队列
  20. end = time.perf_counter()
  21. print(f'执行用时: {end-start}s')

使用多进程的方式之后,代码执行需要2s

使用多进程的方式改进自动化monkey代码

  1. import subprocess
  2. import multiprocessing
  3. def get_connect_devices():
  4. """
  5. 获取已经成功连接的设备
  6. :return: list
  7. """
  8. devices = []
  9. proc = subprocess.Popen('adb devices', stdout=subprocess.PIPE,shell=True)
  10. for line in proc.stdout.readlines():
  11. # print(type(line),line)
  12. # 将字节类型转换为字符串
  13. linestr = line.decode(encoding='utf8')
  14. if '\tdevice' in linestr:
  15. # 字符串分割 提取 deviceid值
  16. device_id = linestr.strip().split('\tdevice')[0]
  17. devices.append(device_id)
  18. return devices
  19. def run(device_id:str):
  20. # 字符串名字去掉特殊字符
  21. devicename = device_id.replace(':','_')
  22. devicefile = devicename.replace('.',"_")
  23. monkey_cmd = f"adb -s {device_id} shell monkey -p org.cnodejs.android.md -s 1625569785406 -vv --throttle 500 500"
  24. with open(f'./{devicefile}.log',mode='w',encoding='utf8') as file:
  25. subprocess.Popen(monkey_cmd,stdout=file,shell=True)
  26. if __name__ == '__main__':
  27. devices = get_connect_devices()
  28. process = []
  29. for device_id in devices:
  30. p = multiprocessing.Process(target=run,args=(device_id,))
  31. process.append(p)
  32. p.start()
  33. for proc in process:
  34. proc.join()
  35. # for device in devices:
  36. # run(device)

appium 多台设备并行执行

现在有两台手机设备

  1. C:\Users\zengy>adb devices
  2. List of devices attached
  3. 127.0.0.1:62001 device
  4. 127.0.0.1:62025 device

分别启动两个appium 服务

  1. appium -p 4723
  1. appium -p 4725

image.png
同时使用appium 连接两台设备

  1. def start_driver(device_id, port):
  2. desired_caps = {
  3. 'platformName': 'Android', # 测试Android系统
  4. 'udid': device_id, # adb devices 命令查看 设置为自己的设备
  5. 'automationName': 'UiAutomator2', # 自动化引擎
  6. 'noReset': False, # 不要重置app的状态
  7. 'fullReset': False, # 不要清理app的缓存数据
  8. # 'chromedriverExecutable': chromedriver, # chromedriver 对应的绝对路径
  9. 'appPackage': "org.cnodejs.android.md", # 应用的包名
  10. 'appActivity': ".ui.activity.LaunchActivity" # 应用的活动页名称
  11. }
  12. driver = webdriver.Remote(f'http://127.0.0.1:{port}/wd/hub', desired_capabilities=desired_caps)
  13. driver.implicitly_wait(5) # 全局的隐式等待时间
  14. if __name__ == '__main__':
  15. deivces = [('127.0.0.1:62001', 4723),('127.0.0.1:62025',4725)]
  16. processes = []
  17. for device in deivces:
  18. p= multiprocessing.Process(target=start_driver,args=(device[0],device[1],))
  19. p.start()
  20. processes.append(p)
  21. for proc in processes:
  22. proc.join()

自动获取连接的设备,并自动启动appium

  1. # 并行 运行两台设备
  2. from appium import webdriver
  3. import subprocess,os,sys
  4. import multiprocessing
  5. def get_connect_devices():
  6. """
  7. 获取已经成功连接的设备
  8. :return: list
  9. """
  10. devices = []
  11. port = 4723
  12. proc = subprocess.Popen('adb devices', stdout=subprocess.PIPE,shell=True)
  13. for line in proc.stdout.readlines():
  14. # print(type(line),line)
  15. # 将字节类型转换为字符串
  16. linestr = line.decode(encoding='utf8')
  17. if '\tdevice' in linestr:
  18. # 字符串分割 提取 deviceid值
  19. device_id = linestr.strip().split('\tdevice')[0]
  20. devices.append((device_id,port))
  21. port += 2 # 端口递增2
  22. return devices
  23. def start_appium(port):
  24. """
  25. 使用命令行的方式启动appium
  26. :return:
  27. """
  28. start_cmd=f'appium -p {port}'
  29. with open(f'./{port}.log',mode='w',encoding='utf8') as file:
  30. subprocess.Popen(start_cmd,shell=True,stdout=file)
  31. def kill_appium(port):
  32. """
  33. 通过端口号结束appium 进程
  34. Windows 执行命令 netstat -anp | finstr port
  35. 通过 taskkill -f -pid pid 结束进程
  36. Mac 系统 执行名 lsof -p port
  37. 执行 kill -9 pid 结束进程
  38. :param port:
  39. :return:
  40. """
  41. # 执行根据端口号 查找进程
  42. # windows 系统:
  43. # print(sys.platform)
  44. if sys.platform == 'win32':
  45. find_pid = f'netstat -ano | findstr {port}'
  46. proc = subprocess.Popen(find_pid,shell=True,stdout=subprocess.PIPE)
  47. for line in proc.stdout.readlines():
  48. print("line",line)
  49. if line:
  50. strline= line.decode(encoding='utf8')
  51. if "LISTENING" in strline:
  52. print('strline',strline)
  53. pid = strline.strip().split('LISTENING')[-1].strip()
  54. # 有了pid 之后 再执行 taskkill -f -pid pid
  55. os.system(f'taskkill -f -pid {pid}')
  56. # mac 系统
  57. else:
  58. mac_cmd = f"lsof -i tcp:{port}"
  59. p = subprocess.Popen(mac_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  60. for line in p.stdout.readlines():
  61. line = line.decode('utf8')
  62. # print("line",line)
  63. if "node" in line:
  64. stdoutline = line.split(" ")
  65. print(stdoutline)
  66. pid = stdoutline[4]
  67. os.system(f"kill {pid}")
  68. def start_driver(device_id, port):
  69. # 先开启appium 再连接, 启动之前 先结束之前启动的apppium
  70. kill_appium(port)
  71. start_appium(port)
  72. desired_caps = {
  73. 'platformName': 'Android', # 测试Android系统
  74. 'udid': device_id, # adb devices 命令查看 设置为自己的设备
  75. 'automationName': 'UiAutomator2', # 自动化引擎
  76. 'noReset': False, # 不要重置app的状态
  77. 'fullReset': False, # 不要清理app的缓存数据
  78. # 'chromedriverExecutable': chromedriver, # chromedriver 对应的绝对路径
  79. 'appPackage': "org.cnodejs.android.md", # 应用的包名
  80. 'appActivity': ".ui.activity.LaunchActivity" # 应用的活动页名称
  81. }
  82. driver = webdriver.Remote(f'http://127.0.0.1:{port}/wd/hub', desired_capabilities=desired_caps)
  83. driver.implicitly_wait(5) # 全局的隐式等待时间
  84. if __name__ == '__main__':
  85. devices = get_connect_devices()
  86. # print(devices)
  87. # deivces = [('127.0.0.1:62001', 4723),('127.0.0.1:62025',4725)]
  88. processes = []
  89. for device in devices:
  90. p= multiprocessing.Process(target=start_driver,args=(device[0],device[1],))
  91. p.start()
  92. processes.append(p)
  93. for proc in processes:
  94. proc.join()

Windows 搭建JenKins自动化测试集成环境

自动化操作多台设备