• addTests 的用法应该是这样的吧,suite.addTests([UserTestCase2("testCase3"), UserTestCase2("testCase1"), UserTestCase("testCase1")])

    suite.addTests(DevOpsRoleCases) 这个看起来是加载了一个类?应该具体到这个类(DevOpsRoleCases)里面的用例吧?
    想要全量执行某个类中的所有用例请用 unittest.defaultTestLoader.discover

  • 还有一个问题是,如果集成到框架里,5037 端口也是 appium 所需要构建 session 连接的,这个图像识别完之后会导致 appium 的 driver 断开链接

  • 我试了下,里面集成了 airtest 的图像识别。成功了一两次,可能还是图标太小了。不过方法还是很好的😀

  • 图像识别不靠谱,还是老老实实的让开发加 id 吧😅 关闭的按钮太小了,识别不到

  • 广告的样式很多种的,大小宽度都会变化,不是固定的坐标就行的,目前感觉 appium 不能操作的话只能靠图像识别返回坐标位置再去点击操作

  • 前言

    问题来源: 当我们执行 UI 自动化测试时可能会涉及到,一台手机跑多个用例的过程亦或者多个 APP 的过程测试,但是一台手机只允许一个模块或者一个 APP 的自动化测试,因此 JOB 之间存在着手机资源的争抢问题,为了防止两个模块被同时进行(因为可能不是自己操作,自己操作会主动去看下,但是不清楚的业务测试同学我们需要帮他们去规避这个问题)

    一、安装插件

    安装 Lockable Resources 插件,插件的主要功能
    在这里插入图片描述
    安装完毕后进行重启 Jenkins

    配置插件

    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    根据自己需要给资源起个 Name 和标签即可,应用并保存

    配置 JOB

    1. 给需要加锁的 JOB 配置好锁名,如图 UI_Auto_Search JOB 构建时需要占用到 search 锁,且占用资源为 1 在这里插入图片描述
    2. 我们把另外一个 JOB(BBJ_Auto_Search)也配置为同样的 search 锁占用资源也为 1

    结果展示

    当 UI_Auto_Search 执行时,BBJ_Auto_Search 被放在构建队列中,此时资源不足,需要等待构建,我们也可以主动去释放资源,释放完,可以正常构建。
    在这里插入图片描述
    在这里插入图片描述

  • 目前是不需要一个 job 执行完去执行另一个,只是为了防止有时两个 job 被不同的人都在执行同一台手机,导致打架

  • 😀 谢谢提供建议,很中肯。第二点感觉可以满足需求


  • //span[@class='ivu-radio']/span[@text='zhangwang1'] 我这边看错了 @class='ivu-radio' 和你想要的 zhangwang1 元素是同级
    所以把 //span[@class='ivu-radio'] 部分内容改成能确定到你要的 span 的上级就可以了,你这边截图内容没显示全

  • http://testerhome.com/topics/32640 想说的是这个?

  • //span[@class='ivu-radio']/span[@text='zhangwang1'] 可以试下,很久没玩 selenium 了😂

  • http://testerhome.com/topics/32639 可以看下这篇帖子内容

  • Appium 并发设备执行 at 2022年03月11日

    好的,我发在楼下了😀

  • Appium 并发设备执行 at 2022年03月11日

    目录层级结构图

    在这里插入图片描述

    主执行文件(runner.py)

    from concurrent.futures.process import ProcessPoolExecutor
    import os
    import time
    import pytest
    import yaml
    from meet.public import *
    from meet.concurrent_startup import *
    
    devices_list = ['Your device uid', 'Your device uid']
    
    
    def runnerPool():
        with open('kyb_caps.yaml', 'r', encoding="utf-8")as file:
            data = yaml.load(file, Loader=yaml.FullLoader)
        print(data)
        devices_Pool = data
        with ProcessPoolExecutor(len(devices_Pool)) as pool:
            pool.map(runPytest, devices_Pool)
    
    
    def runPytest(device):
        print(f"cmdopt is {device}")
        report = f"report-{device['deviceName']}".split(":", 1)[0]
        report_dir = mkdir_dir(report)
        now_time = time.strftime("%H-%M-%S", time.localtime())
        print(f"pool run device is {device['deviceName']}")
        pytest.main(["-s", "./TestCases/", f"--cmdopt={device}", "--alluredir", f"./{report_dir}/{now_time}/xml"])
        time.sleep(1)
        os.system(f"allure generate ./{report_dir}/{now_time}/xml -o ./{report_dir}/{now_time}/html")
    
    
    if __name__ == '__main__':
        """
        获取服务的端口
        并发启动appium的服务
        多设备启动执行用例
        release端口
        """
        ports = []
        appium_start_sync()
        runnerPool()
        for i in range(len(devices_list)):
            port_num = 4723 + 2 * i
            ports.append(port_num)
        print(ports)
        for t in ports:
            release_port(t)
    
    

    公有函数(public.py)

    import time
    
    
    def mkdir(path):
        """创建目录函数"""
        import os
        path = path.strip()   # 去除首位空格
        path = path.rstrip("\\")  # 去除尾部 \ 符号
        is_exists = os.path.exists(path)  # 判断路径是否存在
        if not is_exists:
            os.makedirs(path)  # 不存在目录则创建
            print(path + "创建成功")
            return True
        else:
            print(path + "目录已存在")  # 如果目录存在则不创建,并提示目录已存在
            return False
    
    
    def mkdir_dir(path):
        """创建当天相应的目录"""
        now_date = time.strftime("%Y-%m-%d", time.localtime())
        main_path = f"{path}"
        global dir_path
        dir_path = (main_path + '/' + now_date)
        mkdir(dir_path)
        return dir_path
    
    
    def get_failed_screenshot(self, current_case_name):
        """获取失败截图函数"""
        now_date = time.strftime("%Y-%m-%d", time.localtime())
        now_time = time.strftime("%Y-%m-%d %H-%M-%S", time.localtime())
        global dir_path
        self.driver.get_screenshot_as_file(dir_path + "/" + now_time + " " + current_case_name + ".png")
    
    

    pytest 的配置文件(conftest.py)

    import pytest
    from appium import webdriver
    
    
    def pytest_addoption(parser):
        parser.addoption("--cmdopt", action="store", default="device", help="None")
    
    
    @pytest.fixture(scope="session")
    def cmdopt(request):
        return request.config.getoption("--cmdopt")
    
    
    @pytest.fixture
    def connectDevice(cmdopt):
        device = eval(cmdopt)
        device_caps = {}
        device_caps["platformVersion"] = device["platformVersion"]
        device_caps["platformName"] = device['platformName']
        device_caps["deviceName"] = device['deviceName']
        device_caps["udid"] = device['udid']
        device_caps["appPackage"] = device['appPackage']
        device_caps["appActivity"] = device['appActivity']
        device_caps["newCommandTimeout"] = device["newCommandTimeout"]
        device_caps["noReset"] = True
        device_caps["noSign"] = True
        device_caps["unicodeKeyboard"] = True
        device_caps["resetKeyboard"] = True
        device_caps["systemPort"] = device["systemPort"]
        remote = "http://127.0.0.1:" + str(device["port"]) + "/wd/hub"
        driver = webdriver.Remote(remote, device_caps)
        yield driver
        driver.close_app()
        driver.quit()
    
    

    Appium 并发开启端口文件(concurrent_startup.py)

    # coding=utf-8
    
    import subprocess
    import socket
    from time import ctime
    import multiprocessing
    import os
    from meet.public import *
    
    
    def check_port(host, port):
        """
        检测指定的端口是否被占用
        :param host:
        :param port:
        :return:
        """
    
        # 创建socket对象
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            s.connect((host, port))
            s.shutdown(2)
        except OSError as msg:
            print('----------port %s is available! ----------' % port)
            print(msg)
            return True
        else:
            print('----------port %s already be in use !-----------' % port)
            return False
    
    
    def release_port(port):
        """
        释放指定的端口
        :param port:
        :return:
        """
    
        # 查找对应端口的pid
        cmd_find = 'netstat -aon | findstr %s' % port
        print(cmd_find)
    
        # 返回命令执行后的结果
        result = os.popen(cmd_find).read()
        print(result)
    
        if str(port) and 'LISTENING' in result:
            # 获取端口对应的pid进程
            i = result.index('LISTENING')
            start = i + len('LISTENING') + 7
            end = result.index('\n')
            pid = result[start:end]
    
            # 关闭被占用端口的pid
            cmd_kill = 'taskkill -f -pid %s' % pid
            print(cmd_kill)
            os.popen(cmd_kill)
    
        else:
            print('----------port %s is available !-----------' % port)
    
    
    def appium_start(host, port):
        """
        启动appium服务
        :param host:
        :param port:
        :return:
        """
    
        bootstrap_port = str(port + 1)
    
        # cmd = 'start /b appium -a ' + host + ' -p ' + str(port) + ' --bootstrap-port ' + str(bootstrap_port)  # linux下
        cmd = 'start node G:\\Appium\\resources\\app\\node_modules\\appium\\build\lib\\main.js -a ' + host + ' -p ' + str(
            port) + ' --bootstrap-port ' + str(bootstrap_port)  # win调试
    
        print('%s at %s' % (cmd, ctime()))
        mkdir("appium_log")
        subprocess.Popen(cmd, shell=True, stdout=open('./appium_log/' + str(port) + '.log', 'a'), stderr=subprocess.STDOUT)
    
    
    def start_appium_action(host, port):
        """
        检测端口是否被占用,如果没有被占用则启动appium服务
        :param host:
        :param port:
        :return:
        """
        if check_port(host, port):
            appium_start(host, port)
            return True
        else:
            print("----------appium %s already start!----------" % port)
            return True
    
    
    def appium_start_sync(devices_list):
        """
        并发启动appium服务
        :return:
        """
        appium_process = []
        # 加载appium进程
        for i in range(len(devices_list)):
            host = '127.0.0.1'
            port = 4723 + 2 * i
            appium = multiprocessing.Process(target=start_appium_action, args=(host, port))
            appium_process.append(appium)
    
        # 启动appium服务
        for appium in appium_process:
            appium.start()
        for appium in appium_process:
            appium.join()
    
    

    用例层(test_demo.py)

    import allure
    from selenium.webdriver.support.wait import WebDriverWait
    from unittest import TestCase
    import pytest
    import time
    from appium import webdriver
    
    
    class TestMyConcurrent(object):
    
        @allure.feature("group_Call")
        @allure.story("search_Case")
        def test_001(self, connectDevice):
            """
            测试用例一
            connectDevice = driver
            :param connectDevice:
            :return:
            """
            time.sleep(5)
            connectDevice.find_element_by_id("yiyayiyayo").click()
            time.sleep(2)
            print("已点击咿呀咿呀哟")
            time.sleep(5)
    
        @allure.feature("group_Call")
        @allure.story("search_Case")
        def test_002(self, connectDevice):
            """
            测试用例二
            :return:
            """
            time.sleep(5)
            connectDevice.find_element_by_id("sousuokuang").click()
            time.sleep(2)
            connectDevice.find_element_by_id("sousuokuang").send_keys("天气")
            time.sleep(2)
            connectDevice.find_element_by_id("sousuo_btn").click()  # 点击搜索
            print("已完成点击搜索")
            time.sleep(5)
            connectDevice.keyevent(4)
            time.sleep(2)
            connectDevice.keyevent(4)
            print("已返回主页")
    
        @allure.feature("group_Call")
        @allure.story("search_Case")
        def test003(self, connectDevice):
            """测试用例三"""
            while True:
                try:
                    connectDevice.find_element_by_xpath("//*[contains(@text,'更多')]").click()
                    break
                except:
                    time.sleep(5)
            print("点击完成更多")
            time.sleep(5)
            connectDevice.keyevent(4)
            print("从更多页面返回主页")
            time.sleep(4)
    
    

    设备信息配置文件(kyb_caps.yaml)

    - platformName: Android
      platformVersion: "10"
      deviceName: 123456gf
      appPackage: package_name
      appActivity: package_home_name
      udid: 123456gf
      systemPort: 8208
      newCommandTimeout: 6000
      port: 4723
    
    
    
    - platformName: Android
      platformVersion: "11"
      deviceName: sdakla9312
      appPackage: package_name
      appActivity: package_home_name
      udid: sdakla9312
      systemPort: 8209
      newCommandTimeout: 6000
      port: 4725
    
    
    
    
    

    运行效果截图

    在这里插入图片描述

  • adb shell dumpsys window | findstr mCurrentFocus 荣耀手机 鸿蒙 2.0 可用 一般我都是用这个命令

  • 没怎么玩桌面自动化,http://www.kikicyo.com/article/27/ 之前看到有个 WinAppDriver,写了个 demo,不知道适不适用你这个

  • 感谢各位大神😁

  • 我想应该是这样的,td[1] 可能是 p 标签下的 td1,可能是 div 标签下的 td1,那这样系统就把所有标签下有 td1 的都拿出来了,因为你前面没有加其他的条件,比如//div[@id='title']/td[1]。

  • shake() 方法未实现,目前没有看到能执行相关功能的命令或方法

  • 有心了,放收藏夹吃灰了

  • 写单元测试的公司多吗? at 2022年01月11日

    国外对单元测试很看重,国内嘛 emmm

  • 弹出一个"javascript:;" 是长啥样的,可以尝试使用 get_cookies() 和 add_cookie(cookie_dict) 的方式看下能不能绕过登录页面

  • 引发这个报错的原因可能是,在刚要点击这个元素时,有一层新的元素刷新了 dom 树,导致要点击的元素上面覆盖了一层弹框,selenium 应该不会提供这种现成的方法,如果很多地方会遇到这个问题,可以自己封装一个方法,并且把执行方式可以换成:
    element = driver.find_element_by_css('div[class*="loadingWhiteBox"]')
    driver.execute_script("arguments[0].click();", element)

    看了下自己博客之前竟然也遇到过这种问题,你可以试看看
    https://blog.csdn.net/Python_BT/article/details/108365605?spm=1001.2014.3001.5501

  • 这不是很好解决么,捕获异常等个几秒去重试点击就好了啊


  • 换个定位方法?XPATH 的用, (//*[@text='确定'])