全新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('\config\company_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_Yt\logs'
    # ClearTestResult(path)
    #write_cookie()
    cases = add_case()
    run_case(cases)
run.py

测试报告一览

 

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

 

posted @ 2020-11-27 19:31  叶先生啊  阅读(366)  评论(0编辑  收藏  举报