• 运用tenacity库来提高自动化用例的稳定性


    一. 案例说明

                                 

    1.1 整体业务场景说明

      1) 设备端为一台智能车载设备,有固定IP,设备开机后有WIFI功能

      2) 设备端的开关状态可以通过继电器的开关状态来控制,本案例中使用python来控制继电器开关状态

      3) 设备操作接口只有在连接上设备WIFI环境下才能访问

      4) 云端接口可在公网环境下访问,但是需要注意的是,端云数据交互时会存在着明显的数据延时现象(业务上合理)

    1.2 本文想讨论的用例场景

      1)设备端操作(步骤1):通过python脚本开启继电器开关,进而完成设备启动

      2)设备端验证(步骤2):设备启动成功后,会自动开启设备wifi,校验项为本地是否能成功连接到设备wifi

      3)云端验证(步骤3):访问云端的查看设备信息接口,校验项为接口信息中是否显示设备为在线状态

    1.3 用例场景的稳定性分析

      1) 上述步骤2中,搜索设备端的wifi, 可能需要多次搜索。连接设备端wifi时,也可能需要多次连接

      2) 上诉步骤3中,因为端云数据交互过程中,存在有数据延时同步的现象。故需要通过反复查询的方式来做校验

    1.4 期望达到的稳定性效果

      1)在设备启动后,期望本地可以尽可能快的连接上设备wifi, 不期望去死等一个可以保证设备一定能成功连接到wifi的时间

      2)在设备启动后,期望在5min内,尽可能快的完成云端设备信息接口的校验,不期望死等5min后再去做接口信息校验

    二. 运用tenacity库

    2.1 编写连接wifi的函数

    # -*- coding: utf-8 -*-
    # @Time    : 2020/11/22 12:32
    # @Author  : chinablue
    # @File    : wifi_helper.py
    
    import logging
    
    import pywifi
    from pywifi import const
    from tenacity import retry
    from tenacity import Retrying, stop_after_attempt, stop_after_delay, wait_fixed
    from tenacity import retry_if_exception_type, before_sleep_log
    
    logger = logging.getLogger(__name__)
    
    
    class WifiNotFoundException(Exception):
        """
        如果wifi名称搜索不到, 则抛出此异常
        """
        pass
    
    
    class WifiConnectException(Exception):
        """
        如果wifi无法连接成功, 则抛出此异常
        """
        pass
    
    
    def my_before_sleep(retry_state):
        logger.error(f"执行函数: {retry_state.fn}, 重试次数: {retry_state.attempt_number}, 重试结果: {retry_state.outcome}")
    
    
    class WifiHelper():
        """
            连接wifi
        """
    
        def __init__(self, wifi_name, wifi_passwd):
            self.wifi_name = wifi_name
            self.wifi_passwd = wifi_passwd
            # 创建一个wifi对象
            self.wifi = pywifi.PyWiFi()
            # 获取无线网卡
            self.itf = self.wifi.interfaces()[0]
    
        @retry(
            retry=retry_if_exception_type(WifiNotFoundException),  # 重试条件
            wait=wait_fixed(1),  # 重试间隔
            stop=stop_after_attempt(5) | stop_after_delay(5),  # 停止重试条件
            reraise=True,  # 重试后如果再抛异常, 抛出的是原生异常
            before_sleep=before_sleep_log(logger, logging.WARNING)
        )
        def search_wifi(self) -> bool:
            """
            搜索wifi名字是否存在
            :return:
            """
            # 扫描wifi,并获取[wifi列表]
            self.itf.scan()
            wifi_list = self.itf.scan_results()
            # 判断[wifi名字]是否在[wifi列表]中
            if self.wifi_name in [wifi.ssid for wifi in wifi_list]:
                return True
            else:
                raise WifiNotFoundException()
    
        def is_connect_success(self) -> bool:
            """
            判断wifi是否已连接成功
            :return:
            """
            if self.itf.status() == const.IFACE_CONNECTED:
                logger.info("wifi连接成功")
                return True
            else:
                raise WifiConnectException()
    
        def conn_wifi(self, retry_interval=1, retry_counts=20, timeout=30) -> None:
            """
            填写配置信息, 并连接wifi
            :param retry_interval: 重试间隔
            :param retry_counts:  重试次数
            :param timeout:  超时时间
            :return:
            """
    
            if self.search_wifi():
                # 端口网卡连接
                self.itf.disconnect()
    
                # 删除配置文件
                self.itf.remove_all_network_profiles()
    
                # 加载配置文件
                profile = pywifi.Profile()  # 配置文件
                profile.auth = const.AUTH_ALG_OPEN  # 需要密码
                profile.akm.append(const.AKM_TYPE_WPA2PSK)  # 加密类型
                profile.cipher = const.CIPHER_TYPE_CCMP  # 加密单元
                profile.ssid = self.wifi_name  # wifi名称
                profile.key = self.wifi_passwd  # wifi密码
                tmp_profile = self.itf.add_network_profile(profile)
    
                # 连接wifi
                self.itf.connect(tmp_profile)
    
                r = Retrying(
                    retry=retry_if_exception_type(WifiConnectException),  # 重试条件
                    wait=wait_fixed(retry_interval),  # 重试间隔
                    stop=stop_after_attempt(retry_counts) | stop_after_delay(timeout),  # 停止重试条件
                    reraise=True,  # 重试后如果再抛异常, 抛出的是原生异常
                    before_sleep=before_sleep_log(logger, logging.WARNING)
                )
                try:
                    r(r, self.is_connect_success)
                except Exception as e:
                    raise Exception(f"wifi连接失败, wifi名称: {self.wifi_name}, wifi密码: {self.wifi_passwd}, 异常信息: {e}")
                finally:
                    pass

    注意事项:

      1)本示例中有两处运用了tenacity库的重试功能,分别是search_wifi函数进行重试conn_wifi函数中的部分代码块进行重试

      2)本示例中的重试条件是由指定的异常触发的,所以示例中先自定义了两个异常:WifiNotFoundException,WifiConnectException

      3)搜索wifi的重试逻辑说明:最大重试次数为5次,最大执行时间为5秒,重试间隔为1s

      4)连接wifi的重试逻辑说明:最大重试次数为20次,最大执行时间为30秒,重试间隔为1s

    2.2 编写反复查询接口的校验函数

    # -*- coding: utf-8 -*-
    # @Time    : 2020/11/22 12:32
    # @Author  : chinablue
    # @File    : validate.py
    
    from tenacity import retry, retry_if_result, wait_fixed, stop_after_attempt, stop_after_delay, before_sleep_log
    
    
    def get_vehicle_info(device_id):
        """
        业务接口:查询云端设备信息的接口,一般通过requests库来对业务接口进行封装
        :param device_id: 
        :return: 
        """
        pass
    
    
    def is_onlineStatus(value):
        # 假如提取的信息我们用变量onlineStatusValue表示
        onlineStatusValue = "value为接口返回的json信息,可以通过objectpath库来提取value中的信息"
        if onlineStatusValue == "离线":
            return 1
        else:
            return None
    
    
    @retry(
        retry=retry_if_result(is_onlineStatus),
        wait=wait_fixed(2),  # 重试间隔
        stop=stop_after_attempt(150) | stop_after_delay(300),
        # 停止重试条件
        reraise=True,  # 重试后如果再抛异常, 抛出的是原生异常
    )
    def validate_device_online(device_id):
        return get_vehicle_info(device_id=device_id)

     注意事项:

      1)本示例中的重试条件是由is_onlineStatus函数的返回值决定,在这个函数中可以对接口的返回信息进行必要的逻辑处理

      2)接口验证的重试逻辑说明最大重试次数为150次,最大执行时间为300秒,重试间隔为2s

      3)本示例中,经多次执行验证发现,如果接口验证能通过时,验证时间在70s左右(意味着业务上端云数据的同步需要70秒左右)

  • 相关阅读:
    ViZDoom深度预测(Depth Prediction)
    刨根问底U3D---从Profile中窥探Unity的内存管理
    关于Android真机调测Profiler
    初探Stage3D(三) 深入研究透视投影矩阵
    初探Stage3D(二) 了解AGAL
    初探Stage3D(一) 3D渲染基础原理
    unity3d优化总结篇
    Unity数据存储路径总结
    CREATE A ENERGY / HEALTH BAR HUD
    CREATE A LOADING SCENE / SPLASH SCREEN
  • 原文地址:https://www.cnblogs.com/reconova-56/p/13945282.html
Copyright © 2020-2023  润新知