pytest 通过设置变量的方式传参

并行运行 pytest 的测试用例

  1. import os
  2. import pytest
  3. import multiprocessing
  4. devices = [('127.0.0.1:62001',4723),('127.0.0.1:62003',4725)]
  5. @pytest.fixture(scope='session',autouse=True)
  6. def session():
  7. # 读取环境变量
  8. print(f'启动服务器{os.environ["udid"]}, {os.environ["port"]}')
  9. yield
  10. print(f'关闭服务器{os.environ["udid"]}, {os.environ["port"]}')
  11. def test_01():
  12. print('执行测试用例')
  13. def run(value):
  14. # 设置环境变量 ('127.0.0.1:62001',4723)
  15. os.environ["udid"] = str(value[0]) # 设置变量的时候要求字符串
  16. os.environ['port'] = str(value[1])
  17. pytest.main(['test_pytest.py', '-s', '-v'])
  18. if __name__ == '__main__':
  19. process = []
  20. for val in devices:
  21. # 针对每个进程设置环境变量
  22. p = multiprocessing.Process(target=run,args=(val,))
  23. p.start()
  24. process.append(p)
  25. for proc in process:
  26. proc.join()

项目代码

配置conftest.py

  1. from appium import webdriver
  2. import pytest
  3. import os,sys,subprocess
  4. from appium.webdriver.webdriver import WebDriver
  5. chromedriver= os.path.join(os.path.dirname(os.path.abspath(__file__)),'drivers/chromedriver.exe')
  6. def stop_appium(port):
  7. """
  8. 停止appium
  9. :param port: 启动的端口号
  10. :return:
  11. """
  12. mac_cmd = f"lsof -i tcp:{port}"
  13. win_cmd = f"netstat -ano | findstr {port}"
  14. # 判断操作系统
  15. os_platform = sys.platform
  16. if os_platform == "win32": #windows 系统
  17. win_p = subprocess.Popen(win_cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
  18. for line in win_p.stdout.readlines():
  19. if line:
  20. line = line.decode('utf8')
  21. if "LISTENING" in line:
  22. win_pid = line.split("LISTENING")[1].strip()
  23. print(f"taskkill -f -pid {win_pid}")
  24. os.system(f"taskkill -f -pid {win_pid}")
  25. else: # unix系统
  26. p = subprocess.Popen(mac_cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
  27. for line in p.stdout.readlines():
  28. line = line.decode('utf8')
  29. # print("line",line)
  30. if "node" in line:
  31. stdoutline = line.split(" ")
  32. print(stdoutline)
  33. pid = stdoutline[4]
  34. os.system(f"kill {pid}")
  35. def start_appium(port):
  36. """
  37. 启动appium 服务
  38. :param port: 服务的端口号
  39. :return:
  40. """
  41. stop_appium(port)
  42. cmd = f"appium -p {port}"
  43. logsdir = os.path.join(os.path.dirname(os.path.abspath(__file__)),"logs")
  44. if not os.path.exists(logsdir):
  45. os.mkdir(logsdir)
  46. subprocess.Popen(cmd,shell=True, stdout=open('./logs/'+str(port)+".log",mode='a',encoding="utf8"),
  47. stderr=subprocess.PIPE)
  48. @pytest.fixture(scope='session',autouse=True)
  49. def driver():
  50. # 启动appium服务
  51. port = os.environ['port']
  52. start_appium(port)
  53. desired_caps = {
  54. 'platformName': 'Android', # 测试Android系统
  55. # 'platformVersion': '7.1.2', # Android版本 可以在手机的设置中关于手机查看
  56. 'udid': os.environ['udid'], # adb devices 命令查看 设置为自己的设备
  57. 'automationName': 'UiAutomator2', # 自动化引擎
  58. 'noReset': False, # 不要重置app的状态
  59. 'fullReset': False, # 不要清理app的缓存数据
  60. 'chromedriverExecutable': chromedriver, # chromedriver 对应的绝对路径
  61. 'appPackage': "org.cnodejs.android.md", # 应用的包名
  62. 'appActivity': ".ui.activity.LaunchActivity" # 应用的活动页名称
  63. }
  64. driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_capabilities=desired_caps)
  65. driver.implicitly_wait(5) # 全局的隐式等待时间
  66. yield driver # 将driver 传递出来
  67. # 所有的用例执行之后
  68. driver.quit()
  69. stop_appium(port)
  70. @pytest.hookimpl(tryfirst=True, hookwrapper=True)
  71. def pytest_runtest_makereport(item, call):
  72. # execute all other hooks to obtain the report object
  73. outcome = yield
  74. # 获取用例的执行结果
  75. rep = outcome.get_result()
  76. # 将执行结果保存到 item 属性中 req.when 执行时
  77. setattr(item, "rep_" + rep.when, rep)
  78. @pytest.fixture(scope='function',autouse=True)
  79. def case_run(driver:webdriver,request):
  80. """
  81. 每个测试用例执行完成之后,如果执行失败截图,截图的名称为测试用例名称+时间格式
  82. :param request:
  83. :return:
  84. """
  85. yield
  86. if request.node.rep_call.failed:
  87. import os,time
  88. screenshots = os.path.join(os.path.dirname(os.path.abspath(__file__)),'screeshots')
  89. if not os.path.exists(screenshots):
  90. os.mkdir(screenshots)
  91. casename:str = request.node.nodeid
  92. print("执行测试用例的名字:",casename)
  93. # 测试用例的名字
  94. # casename = casename.replace('.py::','_')
  95. filename = time.strftime('%Y_%m_%d_%H_%M_%S')+".png"
  96. screenshot_file = os.path.join(screenshots,filename)
  97. # 保存截图
  98. driver.save_screenshot(screenshot_file)

在main.py 文件中设置多进程

  1. import pytest
  2. import os,time
  3. import multiprocessing,subprocess
  4. def get_connect_devices():
  5. """
  6. 获取已经成功连接的设备
  7. :return: list
  8. """
  9. devices = []
  10. port = 4723
  11. proc = subprocess.Popen('adb devices', stdout=subprocess.PIPE,shell=True)
  12. for line in proc.stdout.readlines():
  13. # print(type(line),line)
  14. # 将字节类型转换为字符串
  15. linestr = line.decode(encoding='utf8')
  16. if '\tdevice' in linestr:
  17. # 字符串分割 提取 deviceid值
  18. device_id = linestr.strip().split('\tdevice')[0]
  19. devices.append((device_id,port))
  20. port += 2 # 端口递增2
  21. return devices
  22. def run(device):
  23. # 进程启动之后设置变量
  24. os.environ['udid'] = str(device[0])
  25. os.environ['port'] = str(device[1])
  26. report_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'reports')
  27. if not os.path.exists(report_dir):
  28. os.mkdir(report_dir)
  29. report = str(device[1])+time.strftime('%Y_%m_%d_%H_%M_%S')
  30. reportfile = os.path.join(report_dir, report + '.html')
  31. pytest.main(['testcases', '-s', '-v', f'--html={reportfile}'])
  32. if __name__ == '__main__':
  33. devices = get_connect_devices()
  34. process = []
  35. for device in devices:
  36. # 创建进程
  37. p = multiprocessing.Process(target=run,args=(device,))
  38. p.start()
  39. process.append(p)
  40. for proc in process:
  41. proc.join()