zoukankan      html  css  js  c++  java
  • 全新python selenium unittest 框架介绍

    框架采用python3 + selenium3 + PO + yaml + ddt + unittest等技术编写成基础测试框架,并在uiintest基础上增加了session级用例前置,用例失败重跑,用例失败自动截图,美化了测试报告。能适应日常测试工作需要。

    下图为项目整体结构

    基础方法封装

    # -*- coding = UTF-8 -*-
    # Autohr   : 叶松桥
    # File     : base.py
    # project  : Caps_UI_Test
    # time     : 2020/11/27 18:39
    # Describe : 基础方法
    # ---------------------------------------
    import os,sys
    import psycopg2
    import logging.config
    import time
    sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
    from selenium.webdriver.common.by import By
    from selenium.webdriver.support.wait import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    from selenium.common.exceptions import NoSuchFrameException,NoSuchWindowException,NoAlertPresentException,NoSuchElementException
    
    
    CON_LOG = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))+'/config/log.conf'
    logging.config.fileConfig(CON_LOG)
    logging = logging.getLogger()
    
    
    class BaseView(object):
        def __init__(self, driver):
            self.driver = driver
            self.base_url = 'http://mcenter.test.mall'
            self.timeout = 11
    
        def _open(self, url):
            """
            打开浏览器并访问URL地址
            :param url: url地址
            :return:
            """
            url_ = self.base_url + url
            logging.info("this page is %s" % url_)
            self.driver.maximize_window()
            self.driver.get(url_)
            assert self.driver.current_url == url_, 'Did ont land on %s' % url_
    
        def open(self):
            """
            调用私有函数
            :return:
            """
            self._open(self.url)
    
    
    
        def __locate_Element_selector(self, selector):
            """
            八种定位方式选择
            :param selector: 传入的格式必须为:定位方式,定位元素值,顺序不可改变
            :return: 返回定位方式
            """
            selector_by = selector.split(',')[0].strip()
            selector_value = selector.split(',')[1].strip()
            if selector_by in ('i', 'id'):
                locator = (By.ID, selector_value)
            elif selector_by in ('n', 'name'):
                locator = (By.NAME, selector_value)
            elif selector_by in ('c', 'class'):
                locator = (By.CLASS_NAME, selector_value)
            elif selector_by in ('x', 'xpath'):
                locator = (By.XPATH, selector_value)
            elif selector_by in ('s', 'css'):
                locator = (By.CSS_SELECTOR, selector_value)
            elif selector_by in ('t', 'tag_name'):
                locator = (By.TAG_NAME, selector_value)
            elif selector_by in ('l', 'link_text'):
                locator = (By.LINK_TEXT, selector_value)
            elif selector_by in ('ll', 'partial_link_text'):
                locator = (By.PARTIAL_LINK_TEXT, selector_value)
            else:
                raise Exception('selector Error')
            return locator
    
        def find_element(self, selector):
            """
            单个元素定位
            :param loc:定位方式和元素属性
            :return:
            """
            time.sleep(0.5)
            try:
                loc = self.__locate_Element_selector(selector)
                WebDriverWait(self.driver, 10).until(EC.presence_of_element_located(loc))
                return self.driver.find_element(*loc)
            except:
                logging.error('-------------定位异常{0}--------------'.format(selector))
    
    
        def find_elements(self, selector):
            """
            多个元素定位
            :param loc:定位方式和元素属性
            :return:
            """
            time.sleep(0.5)
            try:
                loc = self.__locate_Element_selector(selector)
                WebDriverWait(self.driver, 10).until(EC.presence_of_element_located(loc))
                return self.driver.find_elements(*loc)
            except:
                logging.error('-------------定位异常{0}--------------'.format(selector))
    
    
        def select_text(self, selector, text):
            """
            点击指定文本
            :param selector:定位到的一组元素
            :param text: 想要点击的元素文本
            :return:
            """
            listtext = self.find_elements(selector)
            for i in listtext:
                if i.text == text:
                    i.click()
                    break
            else:
                logging.error("没有找到想要的元素%s" % text)
    
        def switch_frame(self, loc):
            """
            多表单嵌套切换
            :param loc: 传元素的属性值
            :return: 定位到的元素
            """
            try:
                return self.driver.switch_to_frame(loc)
            except NoSuchFrameException as msg:
                logging.error("查找iframe异常-> {0}".format(msg))
    
        def switch_windows(self, loc):
            """
            多窗口切换
            :param loc:
            :return:
            """
            try:
                return self.driver.switch_to_window(loc)
            except NoSuchWindowException as msg:
                logging.error("查找窗口句柄handle异常-> {0}".format(msg))
    
        def switch_alert(self):
            """
            警告框处理
            :return:
            """
            try:
                return self.driver.switch_to_alert()
            except NoAlertPresentException as msg:
                logging.error("查找alert弹出框异常-> {0}".format(msg))
    
        def get_element_attribute(self, selector: str, value='value') -> str:
            """获取元素属性"""
            ele = self.find_element(selector)
            return ele.get_attribute(value)
    
    
        def execute_script(self, js) -> None:
            """执行js脚本"""
            self.driver.execute_script(js)
    
        def exhibition_element(self, selector: str) -> None:
            """将元素显示到可见窗口中 """
            ele = self.find_element(selector)
            js = "arguments[0].scrollIntoView();"
            self.driver.execute_script(js, ele)
    
        def get_conceal_text(self, selector):
            """获取一组元素文本,包含隐藏元素"""
            ele = self.find_elements(selector)
            textlist = []
            js = "return arguments[0].textContent"
            for i in ele:
                # text = i.get_attribute('textContent')   效果相同
                text = self.driver.execute_script(js, i)
                textlist.append(text)
            return textlist
    
        def addAttribute(self, selector, attributeName, value):
            '''
            封装向页面标签添加新属性的方法
            调用JS给页面标签添加新属性,arguments[0]~arguments[2]分别
            会用后面的element,attributeName和value参数进行替换
            添加新属性的JS代码语法为:element.attributeName=value
            比如input.name='test'
            '''
            ele = self.find_element(selector)
            self.driver.execute_script("arguments[0].%s=arguments[1]" % attributeName, ele, value)
    
        def setAttribute(self, selector, attributeName, value):
            '''
            封装设置页面对象的属性值的方法
            调用JS代码修改页面元素的属性值,arguments[0]~arguments[1]分别
            会用后面的element,attributeName和value参数进行替换
            '''
            ele = self.find_element(selector)
            self.driver.execute_script("arguments[0].setAttribute(arguments[1],arguments[2])", ele, attributeName, value)
    
        def get_pgsql_database(self, sql, database='test库名'):
            """连接数据库,返回查询数据"""
            conn = psycopg2.connect(database=database, user="postgres", password=None, host="192.168.0.202", port="5432")
            cur = conn.cursor()
            cur.execute(sql)
            data = cur.fetchall()
            cur.close()
            logging.info(data)
            return data
    
    
        # 重写定义send_keys方法
        def send_key(self, selector, value, clear_first=True, click_first=True):
            loc = self.__locate_Element_selector(selector)
            try:
                #loc = getattr(self, "_%s" % loc)  # getattr相当于实现self.loc
                if click_first:
                    self.driver.find_element(*loc).click()
                if clear_first:
                    self.driver.find_element(*loc).clear()
                    self.driver.find_element(*loc).send_keys(value)
            except AttributeError:
                logging.error("%s 页面中未能找到 %s 元素" % (self, loc))
    base.py

    公共方法介绍

     

     这里放几个常用的方法示例

    实例化driver

    # -*- coding = UTF-8 -*-
    # Autohr   : 叶松桥
    # File     : driver.py
    # project  : Test_Ui_Yt
    # time     : 2020/11/27 18:52
    # Describe : 
    # ---------------------------------------
    from selenium import webdriver
    
    
    def browser(web=None):
        if web == 'Ie':
            driver = webdriver.Ie()
        elif web == 'Firefox':
            driver = webdriver.Firefox()
        elif web == 'ChromeOptions':
            option = webdriver.ChromeOptions()
            option.add_argument('--no-sandbox')
            #以无头模式运行
            option.add_argument('--headless')
            option.add_argument('--window-size=1920,1080')
            option.add_argument('lang = zh_CN.UTF - 8')
            driver = webdriver.Chrome(chrome_options=option)
        else:
            driver = webdriver.Chrome()
        driver.maximize_window()
        driver.implicitly_wait(5)
        return driver
    driver.py

    读取yaml文件

    # -*- coding = UTF-8 -*-
    # Autohr   : 叶松桥
    # File     : readyaml.py
    # project  : Test_Ui_Yt
    # time     : 2020/11/27 19:02
    # Describe : 
    # ---------------------------------------
    import yaml
    import os
    
    
    class ReadYaml(object):
    
        def __init__(self, yaml_path):
            self.yaml_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + yaml_path
    
        def read_yaml(self):
            """读取yaml文件"""
            with open(self.yaml_path, 'r', encoding='UTF-8')as fp:
                yaml_data = yaml.safe_load(fp)
            return yaml_data
    
    
    if __name__ == '__main__':
    
        data = ReadYaml('configcompany_deposit.yaml').read_yaml()
        print(data['playername'])
    readyaml

    用例前后置

    # -*- coding = UTF-8 -*-
    # Autohr   : 叶松桥
    # File     : myunit.py
    # project  : Test_Ui_Yt
    # time     : 2020/11/27 18:52
    # Describe : 
    # ---------------------------------------
    import time
    import unittest
    import logging
    from common.driver import browser
    from businessview.login_business import LoginBusin
    from common.readini import ReadIni
    from common.screenshot import insert_img
    
    
    
    datapath = '/data/useremail.ini'
    data = ReadIni(datapath)
    
    class StartEnd(unittest.TestCase):
        """用例执行前后置,供TestCase继承"""
        #放这里实现session级前置
        driver = browser(data.get_value('driver'))
        # 使用cookie登录
        # cls.driver.get('http://mcenter.uat.mall/mcenter/main/#/spaLogin')
        # cls.driver.add_cookie({'name': 'SID', 'value': data.get_value('cookie','ds')})
        # 输入账号密码登录
        lg = LoginBusin(driver)
        lg.lgbusiness(data.get_value('user'), data.get_value('password'), 1)
        
        def setUp(self) -> None:
            logging.info('----------开始执行用例-----------')
    
    
        def tearDown(self) -> None:
            #用例出现异常就截图
            for method_name, error in self._outcome.errors:
                if error:
                    insert_img(self.driver)
            self.driver.refresh()
            time.sleep(2)
    myunit

    config模块,存放日志、项目配置文件等等

    [loggers]
    keys=root,infoLogger
    
    [logger_root]
    level=DEBUG
    handlers=consoleHandler,fileHandler
    
    [logger_infoLogger]
    handlers=consoleHandler,fileHandler
    qualname=infoLogger
    propagate=0
    
    [handlers]
    keys=consoleHandler,fileHandler
    
    [handler_consoleHandler]
    class=StreamHandler
    level=INFO
    formatter=form02
    args=(sys.stderr,)
    
    [handler_fileHandler]
    class=FileHandler
    level=INFO
    formatter=form01
    args=('../logs/runlog.log', 'a','utf-8')
    
    [formatters]
    keys=form01,form02
    
    [formatter_form01]
    format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s
    
    [formatter_form02]
    format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s
    log.conf

    yaml文件编写示例

     handle操作层示例

    # -*- coding = UTF-8 -*-
    # Autohr   : 叶松桥
    # File     : transferRecordPage.py
    # project  : Test_Ui_Yt
    # time     : 2020/12/17 16:19
    # Describe : 
    # ---------------------------------------
    import time
    import datetime
    from baseview.base import BaseView
    from common.readyaml import ReadYaml
    
    data = ReadYaml('/config/transferRecord').read_yaml()
    
    
    class TransferRecordPage(BaseView):
        """转账记录模块"""
        url = data['url']
    
        def send_username(self, value):
            """输入账号"""
            return self.find_element(data['username']).send_keys(value)
    
        def click_select(self):
            """点击查询"""
            return self.find_element(data['select']).click()
    
        def click_user_type(self):
            """点击用户类型"""
            return self.find_element(data['user_type']).click()
    
        def click_top_agent(self):
            """点击总代"""
            return self.find_element(data['top_agent']).click()
    
        def click_agent(self):
            """点击代理"""
            return self.find_element(data['agent']).click()
    
        def send_order_number(self, value):
            """输入订单号"""
            return self.find_element(data['order_number']).send_keys(value)
    
        def send_create_time(self, value):
            """输入创建时间"""
            self.addAttribute(data['create_time'], 'id', 456789)
            js = "document.getElementById('456789').removeAttribute('readonly')"
            self.execute_script(js)
            # today = datetime.datetime.strptime(time.strftime("%Y/%m/%d %H:%M:%S", time.localtime()), '%Y/%m/%d %H:%M:%S')
            # last_month = today - datetime.timedelta(days=30)
            # today = str(today).replace('-', '/')
            # last_month = str(last_month).replace('-', '/')
            # value = (last_month + ' - ' + today)
            # print(value)
            return self.find_element(data['create_time']).send_keys(value)
    
        def send_max_money(self, value):
            """输入最大金额"""
            return self.find_element(data['max_money']).send_keys(value)
    
        def click_game_type(self):
            """点击游戏类型"""
            return self.find_element(data['game_type']).click()
    
        def click_game_name(self, value):
            """点击游戏名"""
            return self.select_text(data['game_name'], value)
    
        def click_high_class(self):
            """点击高级查询"""
            return self.find_element(data['high_class']).click()
    
        def get_name_text(self):
            """获取账号文本"""
            return self.get_conceal_text(data['name_text'])
    
        def get_game_text(self):
            """获取游戏文本"""
            return self.get_conceal_text(data['game_text'])
    
        def get_order_text(self):
            """获取订单号文本"""
            return self.find_elements(data['order_text'])
    
        def get_money_text(self):
            """获取金额文本"""
            return self.get_conceal_text(data['money_text'])
    
        def get_size_number(self):
            """获取结果条数"""
            text = self.find_element(data['size_number']).text
            number = text.split(' ')[1]
            return number
    
        def click_next_page(self):
            """点击下一页"""
            return self.find_element(data['next_page']).click()
    
        def click_size(self):
            """点击每页条数"""
            return self.find_element(data['size']).click()
    
        def click_size200(self):
            """点击每页200"""
            return self.find_element(data['size200']).click()
    
        def click_order_type(self):
            """点击订单状态"""
            return self.find_element(data['order_type']).click()
    
        def click_being_processed(self):
            """点击处理中"""
            return self.find_element(data['being_processed']).click()
    
        def click_success(self):
            """点击成功"""
            return self.find_element(data['success']).click()
    
        def click_failed(self):
            """点击失败"""
            return self.find_element(data['failed']).click()
    
        def get_status_text(self):
            """获取状态文本"""
            return self.get_conceal_text(data['status_text'])
    
        def get_time_text(self):
            """获取时间文本"""
            return self.get_conceal_text(data['time_text'])
    transferRecordPage.py

    case示例

    # -*- coding = UTF-8 -*-
    # Autohr   : 叶松桥
    # File     : agentTransfer_test.py
    # project  : Test_Ui_Yt
    # time     : 2020/12/2 14:53
    # Describe : 
    # ---------------------------------------
    import unittest
    import ddt
    from businessview.agent_Transfer_business import AgentTransferBusin
    from common.runfailed import Retry
    from common.readexcel import ReadExcel
    from common.myunit import StartEnd
    
    
    data = ReadExcel('/data/selectagentTransfer.xls').get_data()
    
    @ddt.ddt()
    @Retry(max_n=2)
    class AgentTransfer(StartEnd):
    
        @ddt.data(*data)
        def test_agentTransfer(self, data):
            yw = AgentTransferBusin(self.driver)
            types, value = data
            result = yw.call_select(types, value)
            self.assertTrue(result,'测试失败')
    
    
    if __name__ == '__main__':
        unittest.main()
    agentTransfer_test.py

    业务层示例

    # -*- coding = UTF-8 -*-
    # Autohr   : 叶松桥
    # File     : agent_Transfer_business.py
    # project  : Test_Ui_Yt
    # time     : 2020/12/2 14:08
    # Describe : 
    # ---------------------------------------
    from handle.agentTransferPage import AgentTransferPage
    from baseview.base import logging
    import time
    import datetime
    
    
    class AgentTransferBusin(AgentTransferPage):
    
        def select_name(self, value):
            """账号查询"""
            self.send_username(value)
            self.click_select()
            name_text = self.get_name_text()
            for i in name_text:
                if value not in i:
                    logging.error('-----------账号查询异常,异常账号 % s ' % i)
                    return False
            logging.info('------------账号查询测试通过-----------------')
            return True
    
        def select_order(self, value):
            """订单号查询"""
            self.send_ordernum(value)
            self.click_select()
            order = self.get_order_text()
            logging.info(order)
            if value == order:
                logging.info('------------订单号查询测试通过-----------------')
                return True
            else:
                logging.error('-----------账号查询异常,异常订单号 % s ' % order)
                return False
    
        def select_operator(self, value):
            """操作人查询"""
            self.send_operator(value)
            self.click_select()
            operator_text = self.get_operator_text()
            for i in operator_text:
                if value not in i:
                    logging.error('-----------操作人查询异常,异常操作人 % s ' % i)
                    return False
            logging.info('------------操作人查询测试通过-----------------')
            return True
    
        def select_time(self):
            """时间查询"""
            self.click_time()
            self.click_days30()
            self.click_confirm()
            timevalue = self.get_time_value()
            self.click_select()
            logging.info(timevalue)
            str_start_time = timevalue.split('-')[0].strip()
            str_end_time = timevalue.split('-')[1].strip()
            logging.info(str_start_time)
            logging.info(str_end_time)
            start_time = datetime.datetime.strptime(str_start_time, '%Y/%m/%d %H:%M:%S')
            end_time = datetime.datetime.strptime(str_end_time, '%Y/%m/%d %H:%M:%S')
            time_list = self.get_time_text()
            for i in time_list:
                i = datetime.datetime.strptime(i, '%Y/%m/%d %H:%M:%S')
                if i<start_time or i>end_time:
                    logging.error('-----------时间查询异常,异常时间 % s ' % i)
                    return False
            logging.info('------------时间查询测试通过-----------------')
            return True
    
        def select_type(self):
            """类型查询"""
            self.click_types()
            self.click_huabo()
            type_text1 = self.get_type_text()
            for i in type_text1:
                if i!= '劃撥':
                    logging.error('-----------类型查询异常,异常类型 % s ' % i)
            time.sleep(2)
            self.click_types()
            self.click_huishou()
            type_text2 = self.get_type_text()
            for i in type_text2:
                if i!= '回收':
                    logging.error('-----------类型查询异常,异常类型 % s ' % i)
            logging.info('------------类型查询测试通过-----------------')
            return True
    
        def call_select(self, types, value):
            """调用所有查询"""
            try:
                self.open()
                self.click_size()
                self.click_size200()
                if types == '账号':
                    return self.select_name(value)
                elif types == '订单号':
                    return self.select_order(value)
                elif types == '操作人':
                    return self.select_operator(value)
                elif types == '时间':
                    return self.select_time()
                elif types == '类型':
                    return self.select_type()
            except BaseException as er:
                logging.error('--------出现异常,异常信息 % s ' % er)
                return False
    agent_Transfer_business.py

    testrun模块,执行所有用例

    # _*_ coding:utf-8 _*_
    __author__ = '叶松桥'
    
    import configparser
    import os
    import sys
    import requests
    import unittest
    import time
    sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
    from config.BeautifulReport import BeautifulReport
    #from common.newreport import new_report
    #from common.sendemail import send_email
    
    
    
    def add_case():
        """加载路径下以test.py结尾的测试用例"""
        discover = unittest.defaultTestLoader.discover(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))+'/testcase/', pattern='*test.py')
        return discover
    
    def run_case(all_case):
        """执行所有的测试用例"""
        #获取时间
        now = time.strftime("%Y-%m-%d %H_%M_%S")
        #测试报告名字
        report_path =os.path.dirname(os.path.dirname(os.path.abspath(__file__)))+'/report/' #+ now + 'result.html'
        filename = now + 'result.html'
        result = BeautifulReport(all_case)
        result.report(filename=filename, description='积分商城UI测试报告', log_path=report_path)
        # 调用模块生成最新的报告
        #report = new_report(report_path)
        #send_mail(report) #调用发送邮件模块
    
    def ClearTestResult(path):
        """每次运行前清空测试结果文件"""
        for i in os.listdir(path):
            path_file = os.path.join(path, i)
            if os.path.isfile(path_file):
                os.remove(path_file)
            else:
                ClearTestResult(path_file)
    
    def write_cookie():
        url = 'http://mcenter.uat.mall/api-mcenter/passport/login.html'
        data = {'username': 'caps1', 'password': 'yt123.', 'authentication': 1}
        r = requests.post(url, data)
        value = r.headers['Set-Cookie'].split(';')[0].split('=', 1)[1]
        r.close()
        cfg = configparser.ConfigParser()
        path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))+'/data/useremail.ini'
        cfg.add_section('ds')
        cfg.set('ds', "cookie", value)
        with open(path, mode="r+", encoding="utf-8") as f:
            cfg.write(f)
    
    
    if __name__ == '__main__':
        # path = r'E:Test_Ui_Ytlogs'
        # ClearTestResult(path)
        #write_cookie()
        cases = add_case()
        run_case(cases)
    run.py

    测试报告一览

     这里不便放所有代码,完整项目代码打赏后可以私信获取

  • 相关阅读:
    Class constructor FileManager cannot be invoked without 'new' in undefined (line undefined, column undefined)
    vscode插件
    面试题
    使用NPOI读取word表格里面的图片
    Postgresql安装过程记录
    .net Core 新增Area的步骤
    kendo grid上的模版示例
    unicode与string之间的转换
    使用yarn安装puppeteer失败的解决方案
    abp第一篇《框架的下载与mysql数据库的切换》
  • 原文地址:https://www.cnblogs.com/5566yesongqiao/p/14049912.html
Copyright © 2011-2022 走看看