Beispiel #1
0
class _RequestsConfig:
    """
    API配置类
    """
    def __init__(self):
        # 引用日志类
        self.__log = Logger('配置接口').get_logger()
        # 获取公共配置
        self.__pro_public = OperateConfig(constant.config_pro_api)
        self.__address = self.__pro_public.get_str('project', 'address')
        self.__timeout = self.__pro_public.get_int('project', 'timeout')
        self.__proxy = self.__pro_public.get_str('project', 'proxy')
        # 接口信息
        self._api_url = None
        self._headers = None
        self._params = None
        self._data = None
        self._cookies = None
        self._files = None
        self._timeout = self.__timeout
        self._proxies = self._set_proxies()

    def _set_url(self, url: str):
        """
        拼接完整接口地址
        :param url: URL
        :return: 完整URL
        """
        self._api_url = '{}{}'.format(self.__address.rstrip('/'), url)
        self.__log.info('URL:{}'.format(self._api_url))

    def set_headers(self, headers: dict):
        if not self._headers:
            self._headers = headers
        else:
            self._headers.update(headers)
        self.__log.info('headers:{}'.format(self._headers))

    def _set_params(self, params: dict):
        self._params = params
        self.__log.info('params:{}'.format(self._params))

    def _set_data(self, data: dict):
        self._data = data
        self.__log.info('data:{}'.format(self._data))

    def set_files(self, files: dict):
        self._files = files
        self.__log.info('files:{}'.format(self._files))

    def set_cookies(self, cookies: dict):
        self._cookies = cookies
        self.__log.info('cookies:{}'.format(self._cookies))

    def _set_proxies(self):
        if self.__proxy:
            proxy = {self.__proxy.split(':')[0]: self.__proxy}
            self.__log.info('proxies:{}'.format(proxy))
            return proxy
    def __init__(self, report_file, if_result_file=False):
        # 获取邮件配置
        self._email = OperateConfig(constant.config_common_path)
        self._s_mtp = self._email.get_str('email', 's_mtp')
        self._port = self._email.get_str('email', 's_mtp_port')
        self._from_man = self._email.get_str('email', 'from')
        self._to_man = self._email.get_str('email', 'to')
        self._authorization_code = self._email.get_str('email',
                                                       'authorization_code')

        self._report_name = report_file
        self._result_file = if_result_file
Beispiel #3
0
 def __init__(self):
     # 引用日志类
     self.__log = Logger('配置接口').get_logger()
     # 获取公共配置
     self.__pro_public = OperateConfig(constant.config_pro_api)
     self.__address = self.__pro_public.get_str('project', 'address')
     self.__timeout = self.__pro_public.get_int('project', 'timeout')
     self.__proxy = self.__pro_public.get_str('project', 'proxy')
     # 接口信息
     self._api_url = None
     self._headers = None
     self._params = None
     self._data = None
     self._cookies = None
     self._files = None
     self._timeout = self.__timeout
     self._proxies = self._set_proxies()
    def __init__(self):
        # 引用日志类
        self._log = Logger("MYSQL").get_logger()

        # 获取数据库配置
        self._db_config = OperateConfig(config_common_path)

        try:
            self._conn = pymysql.connect(
                user=self._db_config.get_str('mysql', 'username'),
                password=self._db_config.get_str('mysql', 'password'),
                host=self._db_config.get_str('mysql', 'host'),
                port=self._db_config.get_str('mysql', 'port'),
                database=self._db_config.get_str('mysql', 'database'))
            self._log.info('成功连接数据库')
        except pymysql.Error as e:
            self._log.error('数据库连接失败:{}'.format(e))
    def __init__(self, db_=0):
        # 引用日志类
        self._log = Logger('REDIS').get_logger()

        # 获取redis配置信息
        self._redis_conf = OperateConfig(config_common_path)

        try:
            pool = redis.ConnectionPool(
                host=self._redis_conf.get_str('redis', 'host'),
                port=self._redis_conf.get_str('redis', 'port'),
                password=self._redis_conf.get_str('redis', 'auth'),
                db=db_,
                decode_responses=True)
            self._conn = redis.StrictRedis(connection_pool=pool)
            self._log.info('成功连接REDIS,db({})'.format(db_))
        except redis.exceptions.RedisError as e:
            self._log.error('REDIS连接失败:{}'.format(e))
    def __init__(self):
        # 引用日志类
        self._log = Logger("ORACLE").get_logger()

        # 获取数据库配置
        self._db_config = OperateConfig(config_common_path)

        self._conn_str = '{}/{}@{}:{}/{}'.format(
            self._db_config.get_str('oracle', 'username'),
            self._db_config.get_str('oracle', 'password'),
            self._db_config.get_str('oracle', 'host'),
            self._db_config.get_str('oracle', 'port'),
            self._db_config.get_str('oracle', 'database'))
        try:
            self._conn = cx_Oracle.connect(self._conn_str)
            self._log.info('成功连接数据库')
        except cx_Oracle.Error as e:
            self._log.error('数据库连接失败:{}'.format(e))
    def setUpClass(cls):

        cls.log.info('{}开始执行{}'.format('=' * 50, '=' * 50))

        # 读取项目配置
        cls.__pro_public = OperateConfig(config_pro_web)
        cls.homepage = cls.__pro_public.get_str('project', 'homepage')

        # 引用随机数据生成类
        cls.faker = FakeData()

        # 引用关联参数读写类
        cls.context = Context()

        # 创建生成器,返回一个迭代器,用于测试用例计数
        cls._count_case = (x for x in range(1, 1000))
Beispiel #8
0
 def __init__(self):
     self._log = Logger('安卓引擎').get_logger()
     # 读取配置
     self._reader = OperateConfig(constant.config_pro_app)
     self._if_wifi = self._reader.get_bool('server', 'if_wifi')
     self._android_mode = self._reader.get_str('server', 'android_mode')
Beispiel #9
0
class AndroidEngine:
    """
    安卓引擎,自动化启动appium服务,自动连接设备,真机可自动切换WiFi连接,获取driver
    1、实例化前请确保模拟器已开启,真机已连接电脑,并且打开调试模式
    2、暂时只支持单机连接启动,若本机同时连接多个设备,则默认连接已连接设备列表第一个设备
    """
    def __init__(self):
        self._log = Logger('安卓引擎').get_logger()
        # 读取配置
        self._reader = OperateConfig(constant.config_pro_app)
        self._if_wifi = self._reader.get_bool('server', 'if_wifi')
        self._android_mode = self._reader.get_str('server', 'android_mode')

    def get_driver(self):
        """
        根据配置获取driver
        :return: driver对象
        """
        self._start_server()
        devices = self._get_device_names()
        version = self._get_android_version(devices[0])
        app_path = publicFunctions.get_apk_path()
        ports = eval(self._reader.get_str('connected', 'server_ports'))
        if self._android_mode == 'simulator':
            desired_caps = DesiredCaps.caps_android_simulator
            desired_caps['platformVersion'] = version
            desired_caps['deviceName'] = devices[0]
            desired_caps['app'] = app_path
            driver = self._get_driver(desired_caps, ports[0])
            return driver
        elif self._android_mode == 'machine':
            desired_caps = DesiredCaps.caps_android_machine
            desired_caps['platformVersion'] = version
            desired_caps['deviceName'] = devices[0]
            desired_caps['app'] = app_path
            driver = self._get_driver(desired_caps, ports[0])
            return driver
        else:
            self._log.error('启动模式配置有误,请确认:{}'.format(self._android_mode))
            self._kill_server()
            sys.exit()

    def quit_driver(self, driver):
        """
        退出驱动程序,断开模拟器连接,杀掉node进程
        :param driver: driver对象
        :return: None
        """
        if self._android_mode == 'simulator':
            self._disconnect_simulators()
        driver.quit()
        self._kill_server()
        sleep(3)
        self._log.info('已退出驱动')

    def _start_server(self):
        """
        使用命令行自动化启动appium server
        :return: driver对象
        """
        if self._android_mode == 'simulator':
            self._connect_simulators()
        devices = self._get_device_names()
        if self._if_wifi is True and self._android_mode == 'machine':
            self._switch_to_wifi()
            devices = [device for device in self._get_device_names() if ':' in device]
        commands = self._create_appium_commands(devices)
        for cmd in commands:
            cmd = r"start {}".format(cmd)
            os.system(cmd)
            sleep(3)
            self._log.info('appium server已启动:{}'.format(cmd))

    def _get_driver(self, desired_caps: dict, port: str):
        """
        获取driver
        :param desired_caps: 连接参数
        :param port: 服务端口
        :return: driver对象
        """
        try:
            driver = webdriver.Remote(command_executor='http://127.0.0.1:{}/wd/hub'.format(port),
                                      desired_capabilities=desired_caps)
            sleep(1)
            self._log.info('appium server已连接')
            return driver
        except WebDriverException as e:
            self._log.error('appium server连接失败:{}'.format(e))
            sys.exit()

    def _connect_simulators(self):
        """
        对于模拟器,在启动后可以调用此方法实现自动连接电脑
        :return: None
        """
        simulators = self._reader.get_str('server', 'simulator').split(';')
        for simulator in simulators:
            cmd = 'adb connect {}'.format(simulator)
            os.system(cmd)
            self._log.debug('模拟器({})已连接'.format(simulator))

    def _disconnect_simulators(self):
        """
        断开全部已连接模拟器设备
        :return: None
        """
        devices = self._reader.get_str('server', 'simulator').split(';')
        for device in devices:
            cmd = 'adb disconnect {}'.format(device)
            os.system(cmd)
            self._log.debug('设备({})已断开'.format(device))

    def _switch_to_wifi(self):
        """
        对于真机,若需要使用WiFi连接模式,在手机用USB线连接到电脑打开调试模式后,调用此方法可切换至WIFI连接
        :return: None
        """
        devices = self._get_device_names()
        simulators = self._reader.get_str('server', 'simulator').split(';')
        machines = list(set(devices) - set(simulators))
        ports = self._create_useful_ports(5555, machines)
        for machine, port in zip(machines, ports):
            if str(port) in '|'.join(self._get_device_names()):
                cmd_1 = 'adb -s {} shell ip -f inet addr show wlan0'.format(machine)
                result_1 = self._execute_command(cmd_1)
                ip = re.search(r"inet\s(\d+\.\d+\.\d+\.\d+)", result_1).group(1)
                cmd_2 = 'adb -s {} tcpip {}'.format(machine, port)
                os.system(cmd_2)
                cmd_3 = 'adb connect {}:{}'.format(ip, port)
                result_2 = self._execute_command(cmd_3)
                if 'connected' in result_2:
                    self._log.debug('设备({})成功切换至WIFI连接:{}'.format(machine, result_2.strip()))
                    self._log.warning('请拔掉设备({})USB线!!'.format(machine))
                else:
                    self._log.error('设备({})切换至WIFI连接失败:{}'.format(machine, result_2.strip()))

    def _get_device_names(self):
        """
        获取已连接安卓设备名
        :return: 安卓设备名列表
        """
        cmd = 'adb devices'
        result = self._execute_command(cmd)
        devices = re.findall(r"(.*[^\s])\s*device", result)
        devices.pop(0)
        if devices:
            self._log.debug('获取到已连接设备列表:{}'.format(devices))
            return devices
        else:
            self._log.error('未检测到安卓设备。')
            sys.exit()

    def _get_android_version(self, device: str):
        """
        获取已连接安卓设备版本号
        :param device: 设备名
        :return: 版本号
        """
        cmd = f'adb -s {device} shell getprop ro.build.version.release'
        result = self._execute_command(cmd)
        self._log.debug('获取到设备版本号:{}'.format(result))
        return result.strip()

    def _get_package_and_activity(self, apk_path=publicFunctions.get_apk_path()):
        """
        通过'aapt'命令自动获取appPackage和appActivity
        :param apk_path: apk路径
        :return: appPackage和appActivity
        """
        sdk_path = self._get_sdk_path()
        adb_disk = sdk_path.split(':')[0]
        build_tools_path = os.path.join(sdk_path, 'build-tools')
        aapt_path = os.path.join(build_tools_path, os.listdir(build_tools_path)[0])
        cmd = f'{adb_disk}:&cd {aapt_path}&aapt dump badging {apk_path}'
        result = self._execute_command(cmd)
        package = re.search(r"package: name='([\w\\.]+)'", result).group(1)
        activity = re.search(r"launch.*activity: name='([\w\\.]+)'", result).group(1)
        return package, activity

    def _get_sdk_path(self):
        """
        从PATH环境变量中提取Android SDK路径
        :return: Android SDK路径
        """
        path_env = os.environ['PATH']
        sdk_search = re.search(r'(.+?)\\platform-tools', path_env)
        if sdk_search:
            sdk_path = sdk_search.group(1).split(';')[-1]
            if '%' in sdk_path:
                sdk_path = os.environ[sdk_path.strip('%')]
            return sdk_path
        else:
            self._log.error('Android SDK环境变量未配置!!')
            exit()

    @staticmethod
    def _execute_command(cmd: str):
        """
        执行cmd命令
        :param cmd: cmd命令
        :return: 命令行输出
        """
        with os.popen(cmd) as f:
            result = f.read()
        return result

    def _kill_server(self):
        """
        用于每次执行完毕,杀掉进程
        :return: None
        """
        cmd1 = 'tasklist | find "node.exe"'
        if self._execute_command(cmd1):
            cmd2 = 'taskkill -F -PID node.exe'
            self._execute_command(cmd2)
            self._log.info('杀掉appium server进程')

    def _create_appium_commands(self, devices_list: list):
        """
        创建Appium命令行模式启动命令
        :param devices_list: 设备名列表
        :return: cmd命令列表
        """
        p_port_list = self._create_useful_ports(4723, devices_list)
        bp_port_list = self._create_useful_ports(4900, devices_list)
        self._reader.write_data('connected', 'server_ports', str(p_port_list))
        cmd_list = ['appium -a 127.0.0.1 -p {} -bp {}'.format(
            p_port_list[i], bp_port_list[i]
            ) for i in range(len(devices_list))
        ]
        self._log.debug('已生成启动命令:{}'.format(cmd_list))
        return cmd_list

    def _create_useful_ports(self, start_port: int, devices_list: list):
        """
        根据获取的已连接设备创建指定数量的可用端口
        :param start_port: 起始端口
        :param devices_list: 从命令行自动获取的设备列表
        :return: 可用端口列表
        """
        port_list = []
        cmd = 'netstat -ano | findstr {}'.format(start_port)
        while len(port_list) != len(devices_list):
            if not self._execute_command(cmd):
                port_list.append(start_port)
            start_port += 1
        self._log.debug('已生成可用端口:{}'.format(port_list))
        return port_list
                        paths.append(os.path.join(folder, file))
            else:
                if file.endswith('.yaml'):
                    paths.append(os.path.join(folder, file))
    apis = []
    for filename in paths:
        with open(filename) as fp:
            reader = fp.read()
        all_api = yaml.load(reader, Loader=yaml.FullLoader)
        for key, api in all_api.items():
            if 'pytest' in key:
                apis.append((api.get('name'), api))
    return apis


kw = OperateConfig(constant.config_pro_api).get_str('project',
                                                    'filename_keyword')
_all_api = read_pytest_apis(kw)
logger = Logger('执行API测试').get_logger()


@allure.title('{api_name}')
@pytest.mark.parametrize('api_name,api', _all_api)
# 调用全局fixture处理测试结果,每次全量测试时使用,调试时注释掉
@pytest.mark.usefixtures('bar')
def test_main_api(api_name, api):
    """
    pytest接口测试主入口
    :param api_name: 接口名称
    :param api: 接口信息
    :return: None
    """
class Email:
    """
    发送邮件,主函数:send_email()
    :param report_file: 测试报告文件名,供解析,并发送带附件的HTML邮件
    :param if_result_file: 是否发送API测试结果附件,默认为False,若需要发送该附件,传入True
    """
    def __init__(self, report_file, if_result_file=False):
        # 获取邮件配置
        self._email = OperateConfig(constant.config_common_path)
        self._s_mtp = self._email.get_str('email', 's_mtp')
        self._port = self._email.get_str('email', 's_mtp_port')
        self._from_man = self._email.get_str('email', 'from')
        self._to_man = self._email.get_str('email', 'to')
        self._authorization_code = self._email.get_str('email',
                                                       'authorization_code')

        self._report_name = report_file
        self._result_file = if_result_file

    def _login_mailbox(self):
        """
        登录邮箱
        :return: SMTP对象
        """
        s = smtplib.SMTP_SSL(self._s_mtp)
        s.connect(self._s_mtp, self._port)
        s.ehlo()
        s.login(self._from_man, self._authorization_code)
        return s

    def _get_report_path(self):
        """
        生成测试报告文件路径
        :return: report_path
        """
        return join(constant.report_path, self._report_name)

    def _get_report_html(self):
        """
        解析测试报告文件,返回HTML文本
        :return: HTML文本
        """
        with open(self._get_report_path(), encoding='utf-8',
                  errors='ignore') as fp:
            return fp.read()

    def send_email(self, email_subject):
        """
        发送邮件
        :param email_subject: 邮件主题
        :return:
        """
        # 引用日志类
        log = Logger("发送邮件").get_logger()

        s_mtp = self._login_mailbox()
        log.info('成功登录邮箱:%s' % self._from_man)

        report_path_filename = self._get_report_path()
        log.info('成功获取测试报告路径:%s' % report_path_filename)

        email_content = self._get_report_html()
        log.info('成功解析邮件HTML正文')

        # 邮件带附件申明,并设置邮件头
        log.info('设置邮件头...')
        msg_root = MIMEMultipart('related')
        msg_root['from'] = self._from_man
        msg_root['to'] = ','.join(self._to_man)
        msg_root['Subject'] = Header(email_subject + ' at ' + asctime(),
                                     'utf-8')

        # 设置邮件HTML正文
        log.info('设置HTML正文...')
        msg = MIMEText(email_content, 'html', 'utf-8')
        msg_root.attach(msg)

        # 设置测试报告HTML附件
        log.info('设置HTML附件...')
        report = MIMEText(
            open(report_path_filename, 'rb').read(), 'base64', 'utf-8')
        report.add_header('content-disposition',
                          'attachment',
                          filename=self._report_name)
        msg_root.attach(report)

        # 设置API测试结果EXCEL附件
        if self._result_file:
            log.info('设置EXCEL附件...')
            excel = MIMEApplication(
                open(constant.api_result_excel_path, 'rb').read())
            excel.add_header(
                'Content-Disposition',
                'attachment',
                filename=constant.api_result_excel_path.split('/')[1])
            msg_root.attach(excel)

        # 发送
        log.info('正在发送邮件至:%s' % ','.join(self._to_man))
        try:
            s_mtp.sendmail(self._from_man, self._to_man, msg_root.as_string())
        except smtplib.SMTPException as err:
            log.error('邮件发送出错:' + str(err))
        else:
            log.info('成功邮件发送')
        finally:
            s_mtp.quit()
            log.info('成功退出邮箱')
class DBMySql:
    """
    操作MYSQL
    """
    def __init__(self):
        # 引用日志类
        self._log = Logger("MYSQL").get_logger()

        # 获取数据库配置
        self._db_config = OperateConfig(config_common_path)

        try:
            self._conn = pymysql.connect(
                user=self._db_config.get_str('mysql', 'username'),
                password=self._db_config.get_str('mysql', 'password'),
                host=self._db_config.get_str('mysql', 'host'),
                port=self._db_config.get_str('mysql', 'port'),
                database=self._db_config.get_str('mysql', 'database'))
            self._log.info('成功连接数据库')
        except pymysql.Error as e:
            self._log.error('数据库连接失败:{}'.format(e))

    @property
    def conn(self):
        """
        返回数据库连接实例,可单独PyMySql库其他方法
        :return: 数据库连接实例
        """
        return self._conn

    def disconnect(self):
        """
        断开连接
        :return: None
        """
        self._conn.close()
        self._log.info('成功断开数据库')

    def __del__(self):
        self.disconnect()

    def select_all(self, sql_string: str):
        """
        执行查询sql
        :param sql_string: sql语句
        :return: 元组列表
        """
        c = self._conn.cursor()
        self._log.info('执行查询语句:%s' % sql_string)
        x = c.execute(sql_string)
        datalist = x.fetchall()
        self._log.info('查询结果如下:')
        for data in datalist:
            self._log.debug('第 {} 条数据:{}'.format(
                datalist.index(data) + 1, data))
        c.close()
        return datalist

    def select_one(self, sql_string: str):
        """
        执行查询sql
        :param sql_string: sql语句
        :return: 单个查询字段值或单条记录
        """
        c = self._conn.cursor()
        self._log.info('执行查询语句:%s' % sql_string)
        x = c.execute(sql_string)
        # 获取查询的单个字段的值或单条记录
        data = x.fetchone()
        self._log.debug('查询结果如下:{}'.format(data))
        c.close()
        if len(data[0]) == 1:
            return data[0][0]
        else:
            return data

    def execute_sql(self, sql_string: str):
        """
        执行插入、更新、删除操作
        :param sql_string: sql语句
        :return: None
        """
        try:
            c = self._conn.cursor()
            self._log.info('执行%s语句:%s' % (sql_string.split()[0], sql_string))
            c.execute(sql_string)
            self._conn.commit()
            c.close()
        except pymysql.Error as e:
            self._log.error('执行失败:%s' % str(e))
            self._conn.rollback()
            self._log.error('成功回滚操作')
class DBOracle:
    """
    操作ORACLE
    """
    def __init__(self):
        # 引用日志类
        self._log = Logger("ORACLE").get_logger()

        # 获取数据库配置
        self._db_config = OperateConfig(config_common_path)

        self._conn_str = '{}/{}@{}:{}/{}'.format(
            self._db_config.get_str('oracle', 'username'),
            self._db_config.get_str('oracle', 'password'),
            self._db_config.get_str('oracle', 'host'),
            self._db_config.get_str('oracle', 'port'),
            self._db_config.get_str('oracle', 'database'))
        try:
            self._conn = cx_Oracle.connect(self._conn_str)
            self._log.info('成功连接数据库')
        except cx_Oracle.Error as e:
            self._log.error('数据库连接失败:{}'.format(e))

    @property
    def conn(self):
        """
        返回数据库连接实例,可单独cx_Oracle库其他方法
        :return: 数据库连接实例
        """
        return self._conn

    def disconnect(self):
        """
        断开连接
        :return: None
        """
        self._conn.close()
        self._log.info('成功断开数据库')

    def __del__(self):
        self.disconnect()

    def select_all(self, sql_string: str):
        """
        执行查询sql
        :param sql_string: sql语句
        :return: 元组列表
        """
        c = self._conn.cursor()
        self._log.info('执行查询语句:%s' % sql_string)
        x = c.execute(sql_string)
        # 获取全部结果集(元组列表),可使用下标访问结果集,如datalist[0][1]
        datalist = x.fetchall()
        self._log.info('查询结果如下:')
        for data in datalist:
            self._log.debug('第 {} 条数据:{}'.format(
                datalist.index(data) + 1, data))
        c.close()
        return datalist

    def select_one(self, sql_string: str):
        """
        执行查询sql
        :param sql_string: sql语句
        :return: 单个查询字段值或单条记录
        """
        c = self._conn.cursor()
        self._log.info('执行查询语句:%s' % sql_string)
        x = c.execute(sql_string)
        # 获取查询的单个字段的值或单条记录
        data = x.fetchone()
        self._log.debug('查询结果如下:{}'.format(data))
        c.close()
        if len(data[0]) == 1:
            return data[0][0]
        else:
            return data

    def execute_sql(self, sql_string: str):
        """
        执行插入、更新、删除操作
        :param sql_string: sql语句
        :return: None
        """
        try:
            c = self._conn.cursor()
            self._log.info('执行%s语句:%s' % (sql_string.split()[0], sql_string))
            c.execute(sql_string)
            self._conn.commit()
            c.close()
        except cx_Oracle.Error as e:
            self._log.error('执行失败:%s' % str(e))
            self._conn.rollback()
            self._log.error('成功回滚操作')

    def exec_function(self, function_name: str, *parameters,
                      **keyword_parameters):
        """
        执行指定函数,可指定参数
        :param function_name: 函数名
        :param parameters: 元组可变参数
        :param keyword_parameters: 字典可变参数
        :return: None
        """
        try:
            c = self._conn.cursor()
            self._log.info('执行函数:{}'.format(function_name))
            c.callfunc(function_name, *parameters, **keyword_parameters)
            c.close()
        except cx_Oracle.Error as e:
            self._log.error('执行失败:%s' % str(e))
            self._conn.rollback()
            self._log.error('成功回滚操作')

    def exec_process(self, process_name, *parameters, **keyword_parameters):
        """
        执行指定存储过程,可指定参数
        :param process_name: 过程名
        :param parameters: 元组可变参数
        :param keyword_parameters: 字典可变参数
        :return: None
        """
        try:
            c = self._conn.cursor()
            self._log.info('执行过程:{}'.format(process_name))
            c.callproc(process_name, *parameters, **keyword_parameters)
            c.close()
        except cx_Oracle.Error as e:
            self._log.error('执行失败:%s' % str(e))
            self._conn.rollback()
            self._log.error('成功回滚操作')
class DBRedis:
    """
    连接redis执行操作
    :param db_: 库,默认第一个(0)
    """
    def __init__(self, db_=0):
        # 引用日志类
        self._log = Logger('REDIS').get_logger()

        # 获取redis配置信息
        self._redis_conf = OperateConfig(config_common_path)

        try:
            pool = redis.ConnectionPool(
                host=self._redis_conf.get_str('redis', 'host'),
                port=self._redis_conf.get_str('redis', 'port'),
                password=self._redis_conf.get_str('redis', 'auth'),
                db=db_,
                decode_responses=True)
            self._conn = redis.StrictRedis(connection_pool=pool)
            self._log.info('成功连接REDIS,db({})'.format(db_))
        except redis.exceptions.RedisError as e:
            self._log.error('REDIS连接失败:{}'.format(e))

    def conn(self):
        """
        返回连接实例,可单独调用redis库的其他方法
        :return: 连接实例
        """
        return self._conn

    def set_kv(self, key_, value_, ex_=None, px_=None, nx_=False, xx_=False):
        """
        在Redis中设置值,不存在则创建,存在则修改,并返回值;
        对于有些value有业务需求字符串的,加上双引号,如:'"123456"'
        :param key_: key
        :param value_: value
        :param ex_: 过期时间(秒)
        :param px_: 过期时间(毫秒)
        :param nx_: 如果设置为True,则只有name不存在时,当前set操作才执行
        :param xx_: 如果设置为True,则只有name存在时,当前set操作才执行
        :return: 设定的value值
        """
        self._conn.set(key_, value_, ex=ex_, px=px_, nx=nx_, xx=xx_)
        self._log.info('设置成功:{}={}'.format(key_, value_))
        return value_

    def get_v(self, key_):
        """
        获取指定key的value值
        :param key_: key
        :return: value
        """
        value = self._conn.get(key_)
        if value:
            self._log.info('获取到{}={}'.format(key_, value))
            return value
        else:
            self._log.error('键{}不存在'.format(key_))

    def del_kv(self, key_):
        """
        删除指定的key-value
        :param key_: key
        :return:
        """
        value = self._conn.get(key_)
        if value:
            self._conn.delete(key_)
            self._log.info('已删除{}={}'.format(key_, value))
        else:
            self._log.info('指定删除的键不存在')
 def __init__(self):
     # 引用日志类
     self._log = Logger('浏览器引擎').get_logger()
     # 获取配置
     self._reader = OperateConfig(constant.config_pro_web)
class BrowserEngine:
    """
    浏览器引擎,主函数:open_browser()
    """
    def __init__(self):
        # 引用日志类
        self._log = Logger('浏览器引擎').get_logger()
        # 获取配置
        self._reader = OperateConfig(constant.config_pro_web)

    def open_browser(self):
        """
        根据配置决定采用何种模式打开浏览器,获取driver
        :return: driver对象
        """
        headless = self._reader.get_bool('browser', 'headless')
        if headless:
            driver = self._open_browser_with_headless()
        else:
            driver = self._open_browser_without_headless()
        return driver

    def quit_browser(self, driver):
        """
        退出浏览器
        :param driver: driver对象
        :return: None
        """
        driver.quit()
        # 退出浏览器后留缓冲时间,避免立马执行下个case重启浏览器引起进程冲突
        sleep(1.5)
        self._log.info('关闭{}浏览器'.format(driver.name))

    def _open_browser_without_headless(self):
        """
        有界面模式打开浏览器
        :return: driver对象
        """
        driver = None
        browser = self._reader.get_str('browser', 'browser').lower()
        try:
            if browser == 'chrome':
                driver = webdriver.Chrome(constant.chrome_path)
            elif browser == 'firefox':
                driver = webdriver.Firefox(
                    executable_path=constant.firefox_path,
                    service_log_path=devnull)  # 重定向Firefox日志文件至空文件
            elif browser == 'ie':
                driver = webdriver.Ie(constant.ie_path)
            elif browser == 'edge':
                driver = webdriver.Edge(constant.edge_path)
            else:
                self._log.error(f'暂不支持{browser}浏览器!请自行添加!')
                exit()
            try:
                version = driver.capabilities['browserVersion']
            except KeyError:
                version = driver.capabilities['version']
            self._log.info(f'{browser}启动成功,版本号:{version}')
            sleep(1)
            return driver
        except WebDriverException as e:
            self._log.error('{}启动失败:{}'.format(browser, e))
            exit()

    def _open_browser_with_headless(self):
        """
        无头模式打开谷歌或火狐
        :return: driver对象
        """
        driver = None
        browser = self._reader.get_str('browser', 'browser').lower()
        try:
            if browser == 'chrome':
                chrome_options = ChromeOptions()
                chrome_options.add_argument('--headless')
                chrome_options.add_argument('--disable-gpu')
                driver = webdriver.Chrome(options=chrome_options,
                                          executable_path=constant.chrome_path)
            elif browser == 'firefox':
                firefox_options = FirefoxOptions()
                firefox_options.add_argument('--headless')
                firefox_options.add_argument('--disable-gpu')
                driver = webdriver.Firefox(
                    options=firefox_options,
                    executable_path=constant.firefox_path,
                    service_log_path=devnull)
            else:
                self._log.error(f'{browser}配置有误,或{browser}不支持无头模式,请确认!!')
                exit()
            try:
                version = driver.capabilities['browserVersion']
            except KeyError:
                version = driver.capabilities['version']
            self._log.info(f'{browser}启动成功,版本号:{version}')
            sleep(1)
            return driver
        except WebDriverException as e:
            self._log.error('{}无头模式启动失败:{}'.format(browser, e))
            exit()

    def remote_control_browser(self):
        """
        根据配置,获取远程driver
        注:分布式执行用例时,需要先在服务端启动selenium server
        :return: driver对象
        """
        server_address = self._reader.get_str('remote', 'server_address')
        remote_browser = self._reader.get_str('remote',
                                              'remote_browser').upper()
        if hasattr(DesiredCapabilities, remote_browser):
            browser = getattr(DesiredCapabilities, remote_browser)
            try:
                driver = Remote(
                    command_executor='http://{}/wd/hub'.format(server_address),
                    desired_capabilities=browser)
                try:
                    version = driver.capabilities['browserVersion']
                except KeyError:
                    version = driver.capabilities['version']
                self._log.info(f'{browser}启动成功,版本号:{version}')
                return driver
            except WebDriverException as e:
                self._log.error('远程{}启动失败,请确认:{}'.format(remote_browser, e))
                exit()
        else:
            self._log.error(f'{remote_browser}配置有误!')
            exit()