app自动化测试----多设备并行运行

1. 总结

 

 

app自动化测试---多台设备并行运行monkey(subprocss 子进程方式 && multiprocessing 多进程方式)

app自动化测试----使用Python代码启动和关闭 一个/多个设备Appium

app自动化测试---- pytest 多设备连接,并行执行测试用例(pytest 通过设置变量的方式传参)

 

2.样例代码展示

main.py  (项目运行文件

"""
  项目运行文件,并添加测试报告
"""
import pytest
import os,time
import multiprocessing
from utils.adb_handler import get_connect_devices

def run(device):
    # 进程启动之后设置变量
    os.environ['udid'] = str(device[0])
    os.environ['port'] = str(device[1])

    report_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'reports')
    if not os.path.exists(report_dir):
        os.mkdir(report_dir)

    report = time.strftime('%Y-%m-%d_%H-%M-%S') + '_' + str(device[1])

    reportfile = os.path.join(report_dir, report + '.html')

    pytest.main(['testcases', '-s', '-v', f'--html={reportfile}'])



if __name__ == '__main__':
    # 获取所有的连接设备
    devices = get_connect_devices()
    process = []

    for device in devices:
        #创建进程
        p = multiprocessing.Process(target=run,args=(device,))
        p.start()
        process.append(p)

    for proc in process:
        proc.join()

 

conftest.py  (pytest配置文件

 

from appium import webdriver
import os, time,pytest
from utils.adb_handler import start_appium, stop_appium
from utils.file_handler import FileHandler

chromedriver= os.path.join(os.path.dirname(os.path.abspath(__file__)),'drivers/chrome/75.0.3770.140/chromedriver.exe')

# scope='session' 标记的方法执行域为---->所有测试用例运行之前/之后 运行的方法
@pytest.fixture(scope='session',autouse=True)
def driver():
    # 启动appium服务
    port = os.environ['port']
    start_appium(port)

    desired_caps = {
        'platformName': 'Android',                    # 测试Android系统
        'udid': os.environ['udid'],                   # adb devices 命令查看  设置为自己的设备
        'automationName': 'UiAutomator2',             # 自动化引擎
        'noReset': False,                             # 不要重置app的状态
        'fullReset': False,                           # 不要清理app的缓存数据
        'chromedriverExecutable': chromedriver,       # chromedriver 对应的绝对路径
        'appPackage': "org.cnodejs.android.md",       # 应用的包名
        'appActivity': ".ui.activity.LaunchActivity"  # 应用的活动页名称(appium会启动app的加载页)
    }
    driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_capabilities=desired_caps)
    driver.implicitly_wait(5)  # 全局的隐式等待时间
    yield driver  # 将driver 传递出来
    # 所有的用例执行之后
    driver.quit()
    stop_appium(port)


# 该方法是用来获取测试用例执行的结果(passed / FAILED)
@pytest.hookimpl(tryfirst=True,hookwrapper=True)
def pytest_runtest_makereport(item, call):
    outcome = yield
    rep = outcome.get_result()               # 获取用例的执行结果
    # print('用例的执行结果rep---->',rep)
    setattr(item, "rep_" + rep.when, rep)    # 将执行结果保存到 item 属性中 , req.when 执行时


# scope='function' 标记的方法执行域为---->每个测试用例运行之前/之后 运行的方法
@pytest.fixture(scope='function',autouse=True)
def case_run(driver:webdriver,request):   # request 为 pytest_runtest_makereport 方法获取到的执行结果(固定参数和用法)
    yield
    if request.node.rep_call.failed:
        file_handler = FileHandler()
        screenshots = file_handler.save_file_dir('img')
        casename: str = request.node.nodeid
        # print("执行测试用例的名字:", casename)
        # 测试用例的名字很长 testcases/test_ddt/test_ddt_login.py::TestDdtLogin::test_login[....]
        # 对字符串进行截取,截取之后显示为  test_ddt_login-TestDdtLogin
        casename = casename[casename.rfind('/')+1:casename.rfind('::')].replace('.py', '').replace('::', '-')
        filename = time.strftime('%H_%M_%S') + '-' + casename +".png"
        screenshot_file = os.path.join(screenshots, filename)
        # 保存截图
        driver.save_screenshot(screenshot_file)

 

 

 

utils/adb_handler.py   (adb命令处理文件类)

"""
  adb命令处理文件类
"""
import subprocess
import os,sys,time
from utils.file_handler import FileHandler
from config import appoum_port

file_handler = FileHandler()
logs_dir = file_handler.save_file_dir('log')

# 获取所有的连接设备
def get_connect_devices():
    devices = []
    port = appoum_port
    proc = subprocess.Popen('adb devices',stdout=subprocess.PIPE,shell=True)
    for line in proc.stdout.readlines():
        # 将字节类型转换为字符串
        line_str = line.decode(encoding='utf8')
        if '\tdevice' in line_str:
            # 字符串分割 提取 deviceid值
            device_id = line_str.strip().split('\tdevice')[0]
            devices.append((device_id,port))
            port += 2
    print('devices连接设备----》', devices)
    return devices


# 使用命令行的方式启动appium
def start_appium(port):
    """
    启动appium 服务
    :param port: 服务的端口号
    :return:
    """
    stop_appium(port)
    cmd = f"appium -p {port}"
    logsdir = file_handler.save_file_dir('log')
    log_name = time.strftime('%Y_%m_%d') + '-appium_log-' +  str(port) +".log"
    appium_logs_dirName = os.path.join(logsdir,log_name)
    with open(appium_logs_dirName, mode='a', encoding="utf8") as file:
        subprocess.Popen(cmd, shell=True, stdout=file,stderr=subprocess.PIPE)


# 使用命令行的方式关闭appium
def stop_appium(port):
    mac_cmd = f"lsof -i tcp:{port}"
    win_cmd = f"netstat -ano | findstr {port}"
    # 判断操作系统
    os_platform = sys.platform
    print('操作系统:',os_platform)
    # #windows 系统
    if os_platform == "win32":
        win_p = subprocess.Popen(win_cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
        for line in win_p.stdout.readlines():
            if line:
                line = line.decode('utf8')
                if "LISTENING" in line:
                    win_pid = line.split("LISTENING")[1].strip()
                    os.system(f"taskkill -f -pid {win_pid}")
    else:
        # unix系统
        p = subprocess.Popen(mac_cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
        for line in p.stdout.readlines():
            line = line.decode('utf8')
            if "node" in line:
                stdoutline = line.split(" ")
                # print(stdoutline)
                pid = stdoutline[4]
                os.system(f"kill {pid}")

 

posted @ 2021-07-06 16:48  Z_sun  阅读(922)  评论(0编辑  收藏  举报