编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

UI Test Framework 详解(uis tested)

wxchong 2024-08-09 11:25:21 开源技术 25 ℃ 0 评论

本次介绍的UI Test Framework 分为以下几层,UI自动化测试框架主要思想Page Object 的思想,即:将一个页面的元素封装成一个类,本文以禅道项目为例:详情如下:

分层介绍如下:

actions:动作层,封装每个页面的业务。

common:底层,封装一些工具包,以及对Selenium 3 里面的元素定位,鼠标,键盘等动作进行二次封装。

config:配置文件层,存放配置文件,一些主机地址信息,邮件地址,日志地址,驱动地址,日志级别设置,截图地址,一些账号信息等。

element_info:元素信息层,将每个页面的元素封装成类,类似的属性是元素,方法是元素的动作。

element_info_datas:元素信息表层,每个页面的元素信息都单独存放在对应的excel表格中

logs :日志层

reports:报告层

runner:运行层

sample:测试层,用于代码测试

screen_shot:存放截图图片层

test_data:测试数据层

testcases:测试用例集层

webdriver:存放浏览器驱动层

详细说明如下:

common层:

base_page.py对selenium 3框架基本引用进行二次放在都放在这个文件里面

# desc:页面基础类
import os, time
from common.log_utils import logs_obj
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from common.config_utils import config_obj
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.select import Select
from common import HTMLTestReportCN


class BasePage(object):
    def __init__(self, driver):
        # self.driver = webdriver.Chrome()
        self.driver = driver

    def open_url(self, url):
        self.driver.get(url)
        logs_obj.info('打开url地址:{}'.format(url))

    def set_browser_max(self):
        self.driver.maximize_window()
        logs_obj.info('窗口最大化')

    def set_browser_min(self):
        self.driver.minimize_window()
        logs_obj.info('窗口最小化')

    def refresh(self):
        self.driver.refresh()
        logs_obj.info('刷新')

    def close_tab(self):
        self.wait(2)
        self.driver.close()
        logs_obj.info('------- 关闭当前tab页面 --------')

    def quit_driver(self):
        self.wait(1)
        self.driver.quit()
        logs_obj.info('------- 退出浏览器 --------')

    def get_url(self):
        value = self.driver.current_url
        logs_obj.info('------- 获取网页url:{} --------'.format(value))
        return value

    def get_title(self):
        value = self.driver.title
        logs_obj.info('获取网页title:{}'.format(value))
        return value

    # 清空文本框的内容
    def clear(self, element_info):
        self.find_element(element_info).clear()

    # self.username_inputbox = {'element_name': '用户名输入框',
    #                           'locator_type': 'ID',
    #                           'locator_value': 'account',
    #                           'timeout': '5'}
    def find_element(self, element_info):
        '''
        通过 element_info (元素识别的数据,返回一个元素)
        :param element_info:元素识别信息字典
        :return: element
        id,name,class,tag,linktext,plinktext,xpath,css
        '''
        try:
            locator_type_name = element_info['locator_type']
            locator_value_info = element_info['locator_value']
            locator_timeout = element_info['timeout']
            if locator_type_name == 'id':
                locator_type = By.ID
            elif locator_type_name == 'xpath':
                locator_type = By.XPATH
            elif locator_type_name == 'name':
                locator_type = By.NAME
            elif locator_type_name == 'class':
                locator_type = By.CLASS_NAME
            elif locator_type_name == 'css':
                locator_type = By.CSS_SELECTOR
            elif locator_type_name == 'tag':
                locator_type = By.TAG_NAME
            elif locator_type_name == 'linktext':
                locator_type = By.LINK_TEXT
            elif locator_type_name == 'plinktext':
                locator_type = By.PARTIAL_LINK_TEXT

            # self.driver.find_element(By.XPATH,'//li[@data-id="product"]')
            # 显示等待识别元素
            element = WebDriverWait(self.driver, locator_timeout).until(
                lambda x: x.find_element(locator_type, locator_value_info))  # 最核心的代码
            logs_obj.info('[{}] 识别成功'.format(element_info['element_name']))
        except Exception as e:
            logs_obj.info('[{}] 识别不成功,原因是:{}'.format(element_info['element_name'], e.__str__()))
            self.screenshot_as_file()
        return element

    # 封装元素操作方法
    def click(self, element_info):
        element = self.find_element(element_info)
        element.click()
        logs_obj.info('点击:{}'.format(element_info['element_name']))

    # 元素输入操作
    def send_keys(self, element_info, content):
        element = self.find_element(element_info)
        element.send_keys(content)
        logs_obj.info('[{}]中输入{}'.format(element_info['element_name'], content))

    # 获取元素的某个属性值
    def get_attribute(self, element_info):
        title = self.find_element(element_info).get_attribute('title')
        logs_obj.info('title是:{}'.format(title))
        return title

    # 获取元素的某个属性值
    def get_text_value(self, element_info):
        text = self.find_element(element_info).text
        logs_obj.info('text是:{}'.format(text))
        return text

    # 切框架:思路1  通过元素是被数据字典,获取元素再切
    def switch_to_frame_by_element(self, element_info):
        element = self.driver.find_element(element_info)
        self.driver.switch_to.frame(element)
        logs_obj.info('通过元素切换框架,该元素为:{}'.format(element_info['locator_value']))

    # 切框架:思路2  使用id或name切
    def switch_to_frame_id_or_name(self, id_or_name):
        self.driver.switch_to.frame(id_or_name)
        logs_obj.info('通过id或name切换框架,id或name为:{}'.format(id_or_name))

    # 切框架:思路3  把前面两种思路整合,封装成一个统一的方法
    def switch_to_frame(self, **element_dict):
        if 'id' in element_dict.keys():
            self.driver.switch_to.frame(element_dict['id'])
            logs_obj.info('通过id切换框架,id为:{}'.format(element_dict['id']))
        elif 'name' in element_dict.keys():
            self.driver.switch_to.frame(element_dict['name'])
            logs_obj.info('通过name切换框架,name为:{}'.format(element_dict['name']))
        elif 'element_info' in element_dict.keys():
            element = self.find_element(element_dict['element_info'])
            logs_obj.info('通过element_info切换框架,element_info为:{}'.format(element_dict['element_info']))
            self.driver.switch_to.frame(element)
        return element_dict

    def switch_to_default_content(self):
        self.driver.switch_to.default_content()

    # 弹出框封装
    # def switch_to_alert(self,action = 'accept',time_out = config_obj.time_out):
    #     self.wait(time_out)
    #     alert = self.driver.switch_to.alert
    #     alert_text = alert.text
    #     if action == 'accept':
    #         alert.accept()  #确认
    #     else:
    #         alert.dismiss
    #     return alert_text

    def switch_to_alert_2(self, action='accept', time_out=config_obj.time_out):
        WebDriverWait(self.driver, time_out).until(EC.alert_is_present())
        alert = self.driver.switch_to.alert
        logs_obj.info('----------- 切换到弹框 -----------')
        alert_text = alert.text
        logs_obj.info('-------- 弹框的文本内容为:{} -------'.format(alert_text))
        self.wait(3)
        if action == 'accept':
            alert.accept()
            logs_obj.info('-------- 点击弹框的确定按钮! ---------')
        else:
            alert.dismiss()
            logs_obj.info('---------- 点击弹框的取消按钮 -----------')
        return alert_text

    # 切句柄
    # 获取当前页面的句柄
    def get_window_handle(self):
        logs_obj.info('---- 获取窗口的句柄:{}  ---------'.format(self.driver.current_window_handle))
        return self.driver.current_window_handle

    # 切句柄
    def switch_to_window_by_handle(self, window_handle):
        self.driver.switch_to.window(window_handle)
        logs_obj.info('--------  进行切换句柄,句柄为:{} ----------'.format(window_handle))

    # 通过title切
    # def switch_to_window_by_title(self,title):
    #     window_handles = self.driver.window_handles
    #     for window_handle in window_handles:
    #         self.driver.switch_to.window(window_handle)
    #         if self.get_title() == title:
    #             break;

    # 通过url切
    # def switch_to_window_by_url(self,url):
    #     window_handles = self.driver.window_handles
    #     for window_handle in window_handles:
    #         self.driver.switch_to.window(window_handle)
    #         if self.get_url() == url:
    #             break;

    # 切句柄继续封装  加等待时间,只检查标题标题,url的部分内容
    def switch_to_window_by_title(self, title):
        window_handles = self.driver.window_handles
        for window_handle in window_handles:
            self.driver.switch_to.window(window_handle)
            if WebDriverWait(self.driver, config_obj.time_out).until(EC.title_contains(title)):
                logs_obj.info('--------- 通过title({})切换窗口:-----------'.format(title))
                break;

    def switch_to_window_by_url(self, url):
        window_handles = self.driver.window_handles
        for window_handle in window_handles:
            self.driver.switch_to.window(window_handle)
            if WebDriverWait(self.driver, config_obj.time_out).until(EC.title_contains(url)):
                logs_obj.info('--------- 通过url({})切换窗口:-----------'.format(url))
                break;

    # 修改某个元素的属性
    def set_element_attribute(self, element_info, attribute_name, attribute_value):
        js = 'arguments[0].setAttribute("%s","%s");' % (attribute_name, attribute_value)
        el = self.find_element(element_info)
        self.__execute_script(js, el)
        logs_obj.info('[{}],正在执行修改[{}]属性操作,修改成:{}'.format(element_info['element_name'], attribute_value))

    # 移除某个属性
    def remove_element_attribute(self, element_info, attribute_name):
        js = 'arguments[0].removeAttribute("%s");' % attribute_name
        el = self.find_element(element_info)
        self.__execute_script(js, el)
        logs_obj.info('[{}],正在执行移除[{}]属性操作'.format(element_info['element_name'], attribute_name))

    # 滚动条
    def scroll(self, height):
        self.wait(2)
        js = 'window.scrollBy(0,{});'.format(height)  # height:正数向下滚,负数向上滚
        self.__execute_script(js)
        logs_obj.info('正在执行页面滚动操作,滚动的高度为:{}'.format(height))

    # 继续封装 selenium 执行 js 脚本   深入封装
    def __execute_script(self, js, element=None):
        if element:
            self.driver.execute_script(js, element)
            logs_obj.info('[{}]:正在进行js操作,js代码为'.format(element['element_name'], js))
        else:
            self.driver.execute_script(js)
            logs_obj.info('正在执行js,js是{}'.format(js))

    # 固定等待
    def wait(self, s=config_obj.time_out):
        time.sleep(s)
        logs_obj.info('固定等待:%s' % s)

    # 隐式等待
    def implicitly_wait(self, seconds=config_obj.time_out):
        self.driver.implicitly_wait(seconds)
        logs_obj.info('------- 浏览器设置隐式等待:{} --------'.format(seconds))

    # 显示等待

    # 鼠标悬停
    def move_to_element_by_mouse(self, element_info):
        element = self.find_element(element_info)
        ActionChains(self.driver).move_to_element(element)
        logs_obj.info('[{}]:元素进行悬停'.format(element_info['element_name']))

    # 长按不停
    def long_press_element(self, element_info):
        element = self.find_element(element_info)
        ActionChains(self.driver).click_and_hold(element).perform()
        logs_obj.info('[{}]:元素进行长按'.format(element_info['element_name']))

    # 鼠标右击
    def right_click_element(self, element_info):
        element = self.find_element(element_info)
        ActionChains(self.driver).context_click(element).perform()
        logs_obj.info('[{}]:元素进行右击'.format(element_info['element_name']))

    # 截图
    # def screenshot_as_file(self, *screenshot_path):
    #     # 如果没有喘截图路径,则将截图放入默认路径,
    #     if len(screenshot_path) == 0:
    #         screenshot_filepath = config_obj.screen_shot_path
    #     else:
    #         screenshot_filepath = screenshot_path[0]
    #     now = time.strftime('%Y_%m_%d_%H_%M_%S')
    #     current_dir = os.path.dirname(__file__)
    #     screenshot_filepath = os.path.join(current_dir, '..', screenshot_filepath,
    #                                        'UTest_%s.png' % now)
    #     self.driver.save_screenshot(screenshot_filepath)
    #     logs_obj.info(' 截图保存成功,图片位置:{}'.format(screenshot_filepath))

    # 第二次讲截图
    def screenshot_as_file(self):
        '''
        把错误截图放到HTML中
        :return:
        '''
        report_path = os.path.join(os.path.abspath(__file__),'..',config_obj.report_path)
        report_dir = HTMLTestReportCN.ReportDirectory('report_path')
        report_dir.get_screenshot(self.driver)


    # 下拉定位,一下分别通过Select类的索引
    def select_by_element_info(self, element_info):
        element = self.find_element(element_info)
        self.click(element)

    def select_by(self, element_info, **by_index_or_value_or_visible_text):
        element = self.find_element(element_info)
        select = Select(element)
        if 'index' in by_index_or_value_or_visible_text.keys():
            select.select_by_index(by_index_or_value_or_visible_text['index'])
            logs_obj.info('通过select的index定位到该元素,index值为:{}'.format(by_index_or_value_or_visible_text['text']))
        elif 'value' in by_index_or_value_or_visible_text.keys():
            select.select_by_value(by_index_or_value_or_visible_text['value'])
            logs_obj.info('通过select的value定位到该元素,value值为:{}'.format(by_index_or_value_or_visible_text['value']))
        elif 'text' in by_index_or_value_or_visible_text.keys():
            select.select_by_visible_text(by_index_or_value_or_visible_text['text'])
            logs_obj.info('通过select的text定位到该元素,text值为:{}'.format(by_index_or_value_or_visible_text['text']))


if __name__ == '__main__':
    from common.browser import Browser

    driver = Browser().get_driver()
    base_page = BasePage(driver)
    base_page.open_url('https://www.baidu.com')
    base_page.scroll(1000)

browser.py主要对Google、Firefox、msedge三大浏览器驱动进行封装:

浏览器驱动需要下载本地计算机浏览器对应的版本:

# @desc:浏览器封装
import os
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from common.config_utils import config_obj
from selenium.webdriver.chrome.options import Options
from common.log_utils import logs_obj

dri_path = os.path.join(os.path.dirname(__file__), '..', config_obj.get_driver_path)


class Browser():
    def __init__(self, driver_path=dri_path):
        self.__driver_path = driver_path

    def __get_chrome_driver(self):
        chrome_options = Options()
        chrome_options.add_argument('--disable-gpu')  # 谷歌文档提到需要加上这个属性来规避bug
        chrome_options.add_argument('lang=zh_CN.UTF-8')  # 设置默认编码为utf-8
        chrome_options.add_experimental_option('useAutomationExtension', False)  # 取消chrome受自动控制提示
        chrome_options.add_experimental_option("excludeSwitches", ['enable-automation'])  # 取消chrome受自动控制提示
        driver_path = os.path.join(self.__driver_path, 'chromedriver.exe')
        driver_server = Service(driver_path)
        chrome_driver = webdriver.Chrome(service=driver_server, options=chrome_options)
        logs_obj.info('----- 初始化google浏览器,并启动 -----')
        return chrome_driver

    def __get_firefox_driver(self):
        firefox_driver_path = os.path.join(self.__driver_path, 'geckodriver.exe')
        driver_server = Service(firefox_driver_path)
        firefox_driver = webdriver.Firefox(service=driver_server)
        logs_obj.info('----- 初始化firefox浏览器,并启动 -----')
        return firefox_driver

    def __get_edge_driver(self):
        edge_driver_path = os.path.join(self.__driver_path, 'msedgedriver.exe')
        driver_server = Service(edge_driver_path)
        edge_driver = webdriver.Edge(service=driver_server)
        logs_obj.info('----- 初始化edge浏览器,并启动 -----')
        return edge_driver

    def get_driver(self):
        ##设置默认驱动 chrome,firefox,msedge
        if config_obj.get_driver_name == 'chrome':
            driver = self.__get_chrome_driver()
            logs_obj.info('------- 配置文件配置的浏览器驱动是:{} --------'.format(config_obj.get_driver_name))
        elif config_obj.get_driver_name == 'firefox':
            driver = self.__get_firefox_driver()
            logs_obj.info('------- 配置文件配置的浏览器驱动是:{} --------'.format(config_obj.get_driver_name))
        elif config_obj.get_driver_name == 'msedge':
            driver = self.__get_edge_driver()
            logs_obj.info('------- 配置文件配置的浏览器驱动是:{} --------'.format(config_obj.get_driver_name))
        return driver

config_utils.py读取配置文件类:

config.ini文件内容为:

[name]
name1 = test
name2 = dev01
name3 = dev02
name4 = test01
name5 = test02

[password]
password1 = xxxx
password2 = xxxx
password3 = xxxx
password4 = xxxx
password5 = xxxx

[ZenTao_url]
url = http://xx.xxx.xxxx.xx/zentao/www/index.php?m=user&f=login

[default]
driver_path = webdriver

#日志路径
log_path = logs
#日志级别 0 NOTSET    10 DEBUG    20 INFO     30 WARNING    40 ERROR    50 CRITICAL
log_level = 20

#设置默认驱动 chrome,firefox,msedge
driver_name = chrome

#元素识别信息路径
element_info_path = element_info_datas

#默认等待时间
time_out = 30

#默认存放截图目录
screen_shot_path = screen_shot

#默认登录的账户名和密码
username = test01
password = xxxxxxx


#测试数据路径
test_data_path = test_data

#测试用例路径
case_path = testcases

#测试报告路径
report_path = reports/

[email]
#发送服务器
smtp_server = smtp.qq.com
#发送账户
smtp_sender = xxxxx@qq.com
#QQ第三方登录密码
smtp_password = xxxxx
#测接收人
smtp_receiver = xxxxx@qq.com
#抄送人
smtp_cc = 2656343994@qq.com
#邮件标题
smtp_subject = 禅道UI自动化测试报告

config_utils.py类:

# 读取配置文件
import configparser, os

config_path = os.path.join(os.path.dirname(__file__), '../config/config.ini')


class ConfigUtils:
    def __init__(self, cof_path):
        self.config_path = cof_path
        self.conf = configparser.ConfigParser()
        self.conf.read(self.config_path, encoding='utf-8')

    @property
    def get_zentao_url(self):
        return self.conf.get('ZenTao_url', 'url')

    @property
    def get_name_01(self):
        return self.conf.get('name', 'name1')

    @property
    def get_name_02(self):
        return self.conf.get('name', 'name2')

    @property
    def get_password_01(self):
        return self.conf.get('password', 'password1')

    @property
    def get_password_02(self):
        return self.conf.get('password', 'password2')

    @property
    def get_driver_path(self):
        return self.conf.get('default', 'driver_path')

    @property
    def get_log_path(self):
        return self.conf.get('default', 'log_path')

    @property
    def get_log_level(self):
        return int(self.conf.get('default', 'log_level'))

    # 获取默认驱动名称
    @property
    def get_driver_name(self):
        return self.conf.get('default', 'driver_name')

    # 获取默认超时时间
    @property
    def time_out(self):
        return float(self.conf.get('default', 'time_out'))

    # 获取截图默认路径
    @property
    def screen_shot_path(self):
        return self.conf.get('default', 'screen_shot_path')

    @property
    def user_name(self):
        return self.conf.get('default', 'username')

    @property
    def pass_word(self):
        return self.conf.get('default', 'password')

    @property
    def test_data_path(self):
        return self.conf.get('default', 'test_data_path')

    # 测试用例路径
    @property
    def case_path(self):
        return self.conf.get('default', 'case_path')

    # 测试报告路径
    @property
    def report_path(self):
        return self.conf.get('default', 'report_path')

    # 获取email的smtp_server
    @property
    def smtp_server(self):
        return self.conf.get('email', 'smtp_server')

    # 获取email的smtp_sender
    @property
    def smtp_sender(self):
        return self.conf.get('email', 'smtp_sender')

    # 获取email的smtp_password
    @property
    def smtp_password(self):
        return self.conf.get('email', 'smtp_password')

    # 获取email的smtp_receiver
    @property
    def smtp_receiver(self):
        return self.conf.get('email', 'smtp_receiver')

    # 获取email的smtp_cc
    @property
    def smtp_cc(self):
        return self.conf.get('email', 'smtp_cc')

    # 获取email的smtp_subject
    @property
    def smtp_subject(self):
        return self.conf.get('email', 'smtp_subject')


    #element_info_path
    @property
    def element_info_path(self):
        return self.conf.get('default', 'element_info_path')


config_obj = ConfigUtils(config_path)

element_data_utils.py读取excel元素文件类,excel元素文件目录结构如下,文件内容如图:

登录页面元素信息如下图,其它页面元素类似:

element_data_utils.py类

# encoding: utf-8
# @author: newdream_daliu
# @file: element_data_utils.py
# @time: 2022-07-10 17:12
# @desc: 读取页面元素类

import os
import xlrd
from common.config_utils import config_obj

current_path = os.path.dirname(__file__)
excel_path = os.path.join(current_path, '..',config_obj.element_info_path)
#element_info_datas/login/login_page.xlsx
#固定元素的总路径/模块路径/页面名称.xlsx

class ElementDataUtils():
    def __init__(self,module_name,page_name, element_path=excel_path):  # 将页面名称定义
        self.element_path = element_path
        self.element_path = os.path.join(self.element_path,module_name,page_name+'.xlsx')
        self.workbook = xlrd.open_workbook(self.element_path)
        self.sheet = self.workbook.sheet_by_index(0)  # 每次取第一个sheet
        self.row_count = self.sheet.nrows

    def get_element_info(self):
        element_infos ={}
        for i in range(1, self.row_count):
            # if self.sheet.cell_value(i, 2) == page_name:
            element_info = {}
            element_info['element_name'] = self.sheet.cell_value(i, 1)
            element_info['locator_type'] = self.sheet.cell_value(i,2)
            element_info['locator_value'] = self.sheet.cell_value(i, 3)
            timeout_value = self.sheet.cell_value(i,4)
            #方法1:
            # if timeout_value == '':
            #     timeout_value = config_obj.time_out  #如果没有设置,就取config.ini设置的默认值
            # else:
            #     timeout_value = float(timeout_value)  #如果元素表设置了默认值则就转成浮点型
            # element_info['timeout'] = timeout_value
            # 方法2:
            timeout_value = float(timeout_value) if isinstance(timeout_value,float) else config_obj.time_out
            element_info['timeout'] = timeout_value

            element_infos[self.sheet.cell_value(i, 0)] = element_info
        return element_infos

if __name__ == '__main__':
    elements = ElementDataUtils('login','login_page').get_element_info()
    print(elements)
    # elements = ElementDataUtils('main').get_element_info('main_page')
    # print(elements)
    # elements = ElementDataUtils('login').get_element_info('login_page')
    # print(elements)

email_util.py 发送邮件类

import smtplib, os
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from common.log_utils import logs_obj
from email.mime.base import MIMEBase
from email import encoders
from common.config_utils import config_obj
from common import zip_utils


class EmailUtils:
    def __init__(self, smtp_body, smtp_file_path=None):
        self.smtp_server = config_obj.smtp_server
        self.smtp_sender = config_obj.smtp_sender
        self.smtp_password = config_obj.smtp_password
        self.smtp_receiver = config_obj.smtp_receiver
        self.smtp_cc = config_obj.smtp_cc
        self.smtp_subject = config_obj.smtp_subject
        self.smtp_body = smtp_body
        self.smtp_file = smtp_file_path

    def mail_content(self):
        if self.smtp_file != None:
            if self.smtp_file.split('.')[-1].__eq__('zip'):
                return self.__mail_zip_content()
            # elif扩展
            else:
                return self.__mail_text_content()

    def mail_content_by_zip(self):
        report_zip_path = self.smtp_file + '/../禅道UI自动化测试报告.zip'
        zip_utils.zip_dir(self.smtp_file, report_zip_path)
        self.smtp_file = report_zip_path
        message = self.mail_content()
        return message

    def __mail_text_content(self):
        message = MIMEText(self.smtp_body, 'html', 'utf-8')
        message['From'] = self.smtp_sender
        message['To'] = self.smtp_receiver
        message['Cc'] = self.smtp_cc
        message['Subject'] = self.smtp_subject
        return message

    def __mail_zip_content(self):
        message = MIMEMultipart()
        with open(self.smtp_file, 'rb') as f:
            mime = MIMEBase('zip', 'zip', filename=self.smtp_file.split('/')[-1])
            mime.add_header('Content-Disposition', 'attachment',
                            filename=('gb2312', '', self.smtp_file.split('/')[-1]))
            mime.add_header('Content-ID', '<0>')
            mime.add_header('X-Attachment-Id', '0')
            mime.set_payload(f.read())
            encoders.encode_base64(mime)
            message.attach(mime)
        message.attach(MIMEText(self.smtp_body, 'html', 'utf-8'))
        message['From'] = self.smtp_sender
        message['To'] = self.smtp_receiver
        message['Cc'] = self.smtp_cc
        message['Subject'] = self.smtp_subject
        return message

    def zip_send_mail(self):
        try:
            smtp = smtplib.SMTP()
            smtp.connect(self.smtp_server, 25)  # 25 为 SMTP 端口号
            smtp.login(self.smtp_sender, self.smtp_password)
        except:
            smtp = smtplib.SMTP_SSL()
            smtp.login(self.smtp_sender, self.smtp_password)
        mail_content = self.mail_content_by_zip()
        try:
            smtp.sendmail(self.smtp_sender, self.smtp_receiver.split(',') + self.smtp_cc.split(','),
                          mail_content.as_string())
            logs_obj.info('邮件发送人是:{},邮件接收人是;{},邮件抄送人是:{}'.format(self.smtp_sender,
                                                                           self.smtp_receiver.split(',')
                                                                          ,self.smtp_cc.split(',')))
            print("邮件发送成功")
        except smtplib.SMTPException:
            logs_obj.info('邮件发送失败,失败原因:{}'.format(smtplib.SMTPException))
        smtp.close()
        logs_obj.info('发送邮件结束')

excel_util.py读取excel工具包:

# encoding:utf-8
# @author:yangshhiyu
# @file:excel_utils.py
# @time:2022/7/2415:29
# @desc:底层读取excel底层封装
import xlrd, os
from common.config_utils import config_obj

# current_path = os.path.dirname(__file__)
# el_path = os.path.join(current_path, '../element_info_datas/element_info_datas.xlsx')
# test_data_path = os.path.join(current_path,'..',config_obj.test_data_path)

class ExcelUtils():
    def __init__(self, excel_path, sheet_name=None):
        self.excel_path = excel_path
        self.sheet_name = sheet_name
        self.sheet_data = self.__get_sheet_data()

    def __get_sheet_data(self):
        '''
        通过sheet_name 获取一个sheet,如果没有就返回第一个sheet
        :return: sheet  data
        '''
        workbook = xlrd.open_workbook(self.excel_path)
        if self.sheet_name:
            sheet = workbook.sheet_by_name(self.sheet_name)
        else:
            sheet = workbook.sheet_by_index(0)
        return sheet

    @property
    def __get_row_count(self):
        '''获取总的行数'''
        row_count = self.__get_sheet_data().nrows
        return row_count

    @property
    def __get_col_count(self):
        '''获取总的列数 '''
        rol_count = self.__get_sheet_data().ncols
        return rol_count

    def get_sheet_data_by_list(self):
        '''
        通过读取excel中数据
        :return: [[],[],[]....]
        '''
        all_excel_data = []  # 总的数据
        for rownum in range(self.__get_row_count):
            row_excel_data = []  # 一行的数据
            for colnum in range(self.__get_col_count):
                cell_value = self.__get_sheet_data().cell_value(rownum, colnum)
                row_excel_data.append(cell_value)
            all_excel_data.append(row_excel_data)
        return all_excel_data


if __name__ == '__main__':
    current_path = os.path.dirname(__file__)
    el_path = os.path.join(current_path, '../element_info_datas/element_info_datas.xlsx')
    test_data_path = os.path.join(current_path, '..', config_obj.test_data_path)
    datas = ExcelUtils(test_data_path,'login_suit').get_sheet_data_by_list()
    for data in datas:
        print(data)

HTMLTestReportCN.py生产测试报告类,可以自行网上下载:

log_util.py日志文件类:

日志文件目录:

import logging, os,time
from common.config_utils import config_obj

logs_path = os.path.join(os.path.dirname(__file__), '..',config_obj.get_log_path)


class LogUtils(object):
    def __init__(self, logger=None):
        self.log_name = os.path.join(logs_path,'UITest_%s.log'%time.strftime('%Y_%m_%d'))
        self.logger = logging.getLogger(logger)
        self.logger.setLevel(config_obj.get_log_level)  #日志级别,info 级别

        self.fh = logging.FileHandler(self.log_name, encoding='utf-8')
        self.fh.setLevel(config_obj.get_log_level)
        self.ch = logging.StreamHandler()   #写控制带
        self.ch.setLevel(config_obj.get_log_level)

        formatter = logging.Formatter("[%(asctime)s]  - %(filename)s - [line:%(lineno)d] - %(levelname)s: %(message)s")
        self.fh.setFormatter(formatter)
        self.ch.setFormatter(formatter)
        self.logger.addHandler(self.fh)
        self.logger.addHandler(self.ch)
        self.fh.close()
        self.ch.close()

    def info(self, message):
        return self.logger.info(message)

    def error(self, message):
        return self.logger.error(message)

    def debug(self, message):
        return self.logger.debug(message)

    def critical(self, message):
        return self.logger.critical(message)

    def warning(self, message):
        return self.logger.warning(message)


logs_obj = LogUtils()

selenium_base_case.py 测试用例初始化,清理工作封装:

# @desc:unittest 的二次封装
import unittest
from common.config_utils import config_obj
from common.browser import Browser
from common.base_page import BasePage
from common.log_utils import logs_obj

class SeleniumBaseCase(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.url = config_obj.get_zentao_url

    def setUp(self):
        self.driver = Browser().get_driver()
        self.base_page = BasePage(self.driver)
        self.base_page.set_browser_max()
        self.base_page.implicitly_wait()
        self.base_page.open_url(self.url)

    def tearDown(self):
        errors = self._outcome.errors
        for test,exc_info in errors:
            if exc_info:
                self.base_page.wait(3)
                self.base_page.screenshot_as_file()
        logs_obj.info('-----------  测试用例执行完毕  --------')
        self.base_page.quit_driver()

test_data_utils.py读取测试用例测试数据封装:

# encoding:utf-8
# @author:yangshhiyu
# @file:test_data_utils.py
# @time:2022/7/2720:54
# @desc:读取测试用例数据
import os
from common.excel_utils import ExcelUtils
from common.config_utils import config_obj


class TestDataUtils:
    def __init__(self,test_suit_name,test_file_name,test_class_name):
        current_path = os.path.dirname(__file__)
        test_data_path = os.path.join(current_path, '..', config_obj.test_data_path)
        test_data_path = os.path.join(test_data_path,test_suit_name,test_file_name+'.xlsx')
        self.test_class_name = test_class_name
        self.excel_data = ExcelUtils(test_data_path,test_class_name).get_sheet_data_by_list()
        self.excel_row = len(self.excel_data)

    def convert_exceldata_to_testdata(self):
        '''把excel表中的数据转换成用例的字典数据'''
        test_data_infos = {}
        for i in range(1,self.excel_row):
            test_data_info = {}
            # if self.excel_data[i][2].__eq__(self.test_class_name):
            test_data_info['test_name'] = self.excel_data[i][1]
            test_data_info['isnot'] = self.excel_data[i][2]
            test_data_info['isnot'] = False if self.excel_data[i][2].__eq__('是') else True
            test_data_info['expected_result'] = self.excel_data[i][3]
            test_parameter = {}
            for j in range(4,len(self.excel_data[i])):
                if self.excel_data[i][j].__contains__('=') and len(self.excel_data[i][j])>2:
                    parameter_info = self.excel_data[i][j].split('=')
                    test_parameter[parameter_info[0]] = parameter_info[1]
            test_data_info['test_parameter'] = test_parameter
            test_data_infos[self.excel_data[i][0]] = test_data_info
        return test_data_infos

if __name__ == '__main__':
    infos = TestDataUtils('qa_suit','qa_case', 'CreateBugSuit').convert_exceldata_to_testdata()
    for info in infos.values():
        print(info)

zip_utils.py 将测试报告压缩成zip文件,进行封装:

# encoding:utf-8
# @author:yangshhiyu
# @file:test_data_utils.py
# @time:2022/7/2720:54
# @desc:读取测试用例数据
import os
from common.excel_utils import ExcelUtils
from common.config_utils import config_obj


class TestDataUtils:
    def __init__(self,test_suit_name,test_file_name,test_class_name):
        current_path = os.path.dirname(__file__)
        test_data_path = os.path.join(current_path, '..', config_obj.test_data_path)
        test_data_path = os.path.join(test_data_path,test_suit_name,test_file_name+'.xlsx')
        self.test_class_name = test_class_name
        self.excel_data = ExcelUtils(test_data_path,test_class_name).get_sheet_data_by_list()
        self.excel_row = len(self.excel_data)

    def convert_exceldata_to_testdata(self):
        '''把excel表中的数据转换成用例的字典数据'''
        test_data_infos = {}
        for i in range(1,self.excel_row):
            test_data_info = {}
            # if self.excel_data[i][2].__eq__(self.test_class_name):
            test_data_info['test_name'] = self.excel_data[i][1]
            test_data_info['isnot'] = self.excel_data[i][2]
            test_data_info['isnot'] = False if self.excel_data[i][2].__eq__('是') else True
            test_data_info['expected_result'] = self.excel_data[i][3]
            test_parameter = {}
            for j in range(4,len(self.excel_data[i])):
                if self.excel_data[i][j].__contains__('=') and len(self.excel_data[i][j])>2:
                    parameter_info = self.excel_data[i][j].split('=')
                    test_parameter[parameter_info[0]] = parameter_info[1]
            test_data_info['test_parameter'] = test_parameter
            test_data_infos[self.excel_data[i][0]] = test_data_info
        return test_data_infos

if __name__ == '__main__':
    infos = TestDataUtils('qa_suit','qa_case', 'CreateBugSuit').convert_exceldata_to_testdata()
    for info in infos.values():
        print(info)

元素信息层:

登录页面元素信息类,login_page.py:

# desc:登录页面
from common.base_page import BasePage
from common.browser import Browser
from common.element_data_utils import ElementDataUtils
from common.log_utils import logs_obj

elements = ElementDataUtils('login','login_page').get_element_info()


class LoginPage(BasePage):
    def __init__(self,driver):
        # 元素识别分离
        super().__init__(driver)  # 初始化父类构建函授
        logs_obj.info('------- 启动浏览器  -------')

        self.username_input_box = elements['username_input_box']
        self.password_input_box = elements['password_input_box']
        self.login_button = elements['login_button']
        self.keepLogin_checkbox = elements['keepLogin_checkbox']

    def input_username(self, name):
        self.send_keys(self.username_input_box, name)

    def input_password(self, password):
        self.send_keys(self.password_input_box, password)

    def click_login(self):
        self.click(self.login_button)

    def click_keep_login(self):
        self.click(self.keepLogin_checkbox)

    #封装一个登录失败,弹出的提示框中点确认,并返回提示框中的内容
    def get_login_fail_alert_content(self):
        return self.switch_to_alert_2()


if __name__ == '__main__':
    # 驱动处理
    login_page = LoginPage(Browser().get_driver())
    login_page.open_url('http://47.107.178.45/zentao/www/index.php?m=user&f=loginz')
    login_page.set_browser_max()
    login_page.input_username('test01')
    login_page.input_password('newdream13')
    login_page.click_login()
    login_page.get_login_fail_alert_content()
    login_page.quit_driver()

主页面元素信息类,main_page.py

# desc主页面类
from common.browser import Browser
from element_info.login.login_page import LoginPage
from common.element_data_utils import ElementDataUtils
from common.base_page import BasePage
from common.log_utils import logs_obj


class MainPage(BasePage):
    def __init__(self, driver):
        # 元素识别分离
        super().__init__(driver)  # 初始化父类构建函授
        logs_obj.info('------- 启动浏览器  -------')

        # 难点,页面衔接
        elements = ElementDataUtils('main', 'main_page').get_element_info()
        self.company_name_show_box = elements['company_name_show_box']
        self.my_zone_menu = elements['my_zone_menu']
        self.my_product_menu = elements['my_product_menu']
        self.my_project_menu = elements['my_project_menu']
        self.my_qa_menu = elements['my_qa_menu']
        self.username_show_box = elements['username_show_box']
        self.quit_button = elements['quit_button']

    def get_company_name(self):
        self.get_attribute(self.company_name_show_box)

    def goto_myzone(self):
        self.click(self.my_zone_menu)

    def goto_product(self):
        self.click(self.my_product_menu)

    def goto_project(self):
        self.click(self.my_project_menu)

    def goto_qa(self):
        self.click(self.my_qa_menu)

    def get_username(self):
        text = self.get_text_value(self.username_show_box)
        return text

    def click_username_button(self):
        self.click(self.username_show_box)

    def click_quit_button(self):
        self.click(self.quit_button)


if __name__ == '__main__':
    driver = Browser().get_driver()
    LoginPage_obj = LoginPage(driver)
    main_page_obj = MainPage(driver)
    main_page_obj.open_url('http://47.107.178.45/zentao/www/index.php?m=user&f=loginz')
    main_page_obj.set_browser_max()
    # 登录
    LoginPage_obj.input_username('test01')
    LoginPage_obj.input_password('newdream123')
    LoginPage_obj.click_login()
    # 点击我的,项目,产品,
    main_page_obj.get_company_name()
    main_page_obj.get_username()
    main_page_obj.goto_myzone()
    main_page_obj.goto_project()
    main_page_obj.goto_product()
    main_page_obj.goto_qa()
    main_page_obj.click_username_button()
    main_page_obj.click_quit_button()
    LoginPage_obj.quit_driver()

产品信息元素类,product_page.py暂未补充代码。

qa页面元素类,create_bug_page.py

# @desc 提bug元素和动作页面
from common.base_page import BasePage
from common.browser import Browser
from common.log_utils import logs_obj
from common.element_data_utils import ElementDataUtils
from element_info.login.login_page import LoginPage
from element_info.main.main_page import MainPage


class CreateBUGPage(BasePage):
    def __init__(self,driver):
        # 元素识别分离
        super().__init__(driver)  # 初始化父类构建函授
        logs_obj.info('------- 启动浏览器  -------')
        # 难点,页面衔接
        elements = ElementDataUtils('qa', 'create_bug_page').get_element_info()
        self.bug_module_link = elements['bug_module_link']
        self.commit_bug_link = elements['commit_bug_link']
        self.product_selects = elements['product_selects']
        self.Ecommerce_projects_select = elements['Ecommerce_projects_select']
        self.module_box_selects = elements['module_box_selects']
        self.customer_center_select = elements['customer_center_select']
        self.bug_errors_selects = elements['bug_errors_selects']
        self.page_error_select = elements['page_error_select']
        self.bug_os_selects = elements['bug_os_selects']
        self.windows_select = elements['windows_select']
        self.bug_browser_selects = elements['bug_browser_selects']
        self.chrome_select = elements['chrome_select']
        self.edition_selects = elements['edition_selects']
        self.v1_0_select = elements['v1_0_select']
        self.loadAllUsers_button = elements['loadAllUsers_button']
        self.assignedTo_selects = elements['assignedTo_selects']
        self.LanShu_select = elements['LanShu_select']
        self.deadline_input = elements['deadline_input']
        self.bug_title_input = elements['bug_title_input']
        self.severity_selects = elements['severity_selects']
        self.two_severity_select = elements['two_severity_select']
        self.pri_selects = elements['pri_selects']
        self.two_pri_select = elements['two_pri_select']
        self.report_steps_iframe = elements['report_steps_iframe']
        self.report_steps_body = elements['report_steps_body']
        self.storyIdBox_span = elements['storyIdBox_span']
        self.upload_file_button = elements['upload_file_button']
        self.submit_button = elements['submit_button']

    # 点击主页面测试 --->
    def click_bug_module_link(self):
        self.click(self.bug_module_link)

    # 点击提bug按钮
    def click_commit_bug_link(self):
        self.click(self.commit_bug_link)

    def get_commit_bug_text(self):
        bug_text = self.get_text_value(self.commit_bug_link)
        return bug_text

    # 激活系统存在的产品
    def click_product_selects(self):
        self.click(self.product_selects)

    # 选择电商项目产品项
    def click_Ecommerce_projects_select(self):
        self.click(self.Ecommerce_projects_select)

    # 激活产品模块
    def click_module_selects(self):
        self.click(self.module_box_selects)

    # 选择/后台-客户中心模块
    def click_customer_center_select(self):
        self.click(self.customer_center_select)

    # 激活bug错误类型下拉框
    def click_bug_selects(self):
        self.click(self.bug_errors_selects)

    # 点击界面错误项
    def click_page_error_select(self):
        self.click(self.page_error_select)

    # 激活bug系统错误类型
    def click_bug_os_selects(self):
        self.click(self.bug_os_selects)

    # 选择bug错误类型:windows
    def click_bug_windows_select(self):
        self.click(self.windows_select)

    # 激活bug属于什么浏览器错误
    def click_bug_browser_selects(self):
        self.click(self.bug_browser_selects)

    # 选择属于chrome 浏览器
    def click_chrome_select(self):
        self.click(self.chrome_select)

    # 点击版本下拉框
    def click_edition_selects(self):
        self.click(self.edition_selects)

    # 选择v1.0版本
    def click_v1_0_select(self):
        self.click(self.v1_0_select)

    # 点击加载所有用户按钮
    def click_loadAllUsers(self):
        self.click(self.loadAllUsers_button)

    # 激活指派人下拉列表
    def click_assignedTo_selects(self):
        self.click(self.assignedTo_selects)

    # 选择指派人兰输
    def click_LanShu_select(self):
        self.click(self.LanShu_select)

    # 输入截至日期:'2022-08-08'
    def input_deadline(self,deadline):
        self.send_keys(self.deadline_input,deadline )

    # 输入bug标题:'人才中心-新增接口404'
    def input_bug_title(self,bug_title):
        self.send_keys(self.bug_title_input, bug_title)

    # 激活严重等级下拉框
    def click_severity_selects(self):
        self.click(self.severity_selects)

    # 选择严重程度2选项
    def click_two_severity_select(self):
        self.click(self.two_severity_select)

    # 激活优先级下拉框
    def click_pri_selects(self):
        self.click(self.pri_selects)

    # 选择严重程度2选项
    def click_two_pri_select(self):
        self.click(self.two_pri_select)

    # 切入重现步骤框架中
    def enter_report_steps_iframe(self):
        self.switch_to_frame(element_info=self.report_steps_iframe)

    # 清空重现步骤内容
    def clear_report_steps_body(self):
        self.clear(self.report_steps_body)

    # 输入重现步骤内容:'[步骤]:1.登录成功后。2.点击人才中心。3.点击新增按钮。4.输入内容,点击保存按钮。.\n\n'
    #                        '[结果]:点击保存提示:"system error".\n\n'
    #                        '[期望]:点击保存提示:"保存成功"'
    def input_report_steps_body(self,report_steps_content):
        self.send_keys(self.report_steps_body,report_steps_content)

    # 激活相关需求
    def click_storyBox_span(self):
        self.click(self.storyIdBox_span)

    # 点击上传文件按钮
    def click_upload_file_button(self):
        self.click(self.upload_file_button)

    # 点击提交按钮
    def click_submit_button(self):
        self.click(self.submit_button)


if __name__ == '__main__':
    driver = Browser().get_driver()
    LoginPage_obj = LoginPage(driver)
    main_page_obj = MainPage(driver)
    create_bug_page_obj = CreateBUGPage(driver)
    main_page_obj.open_url('http://47.107.178.45/zentao/www/index.php?m=user&f=loginz')
    main_page_obj.set_browser_max()
    # 登录
    LoginPage_obj.input_username('test01')
    LoginPage_obj.input_password('newdream123')
    LoginPage_obj.click_login()
    main_page_obj.goto_qa()
    create_bug_page_obj.click_bug_module_link()
    # create_bug_page_obj.click_commit_bug_link()
    print(create_bug_page_obj.get_commit_bug_text())

动作层,目录结构如下图:

login_action.py

# -- coding: utf-8 --
# @Time : 2022/7/22 17:13
# @Author : siyu.yang
# @File : login_action.py
# @Software: PyCharm
# @desc: 功能层
from element_info.login.login_page import LoginPage
from element_info.main.main_page import MainPage
from common.config_utils import config_obj


class LoginAction():
    def __init__(self, driver):
        self.login_page = LoginPage(driver)

    # 登录操作
    def login_action(self, username, password):
        self.login_page.input_username(username)
        self.login_page.input_password(password)
        self.login_page.click_login()

    def login_success(self, username, password):
        self.login_action(username, password)
        return MainPage(self.login_page.driver)

    def login_fail(self, username, password):
        self.login_action(username, password)
        return self.login_page.get_login_fail_alert_content()

    # 默认登录
    def default_login(self):
        self.login_action(config_obj.user_name, config_obj.pass_word)
        return MainPage(self.login_page.driver)

    # 扩展:
    def login_by_cookie(self):
        pass

main_action.py

# @desc: 主页面业务类
from element_info.login.login_page import LoginPage
from element_info.main.main_page import MainPage


class MainPageAction():
    def __init__(self, driver):
        self.main_page = MainPage(driver)

    # 登录-进入我的地盘-退出
    def goto_myzone_quit(self):
        self.main_page.goto_myzone()
        self.main_page.click_username_button()
        self.main_page.click_quit_button()
        return LoginPage(self.main_page.driver)  # 退出操作返回主页面

    # 登录-点击我的项目-退出
    def goto_project_quit(self):
        self.main_page.goto_project()
        self.main_page.click_username_button()
        self.main_page.click_quit_button()
        return LoginPage(self.main_page.driver)  # 退出操作返回主页面

    # 登录-点击我的产品-退出
    def goto_product_quit(self):
        self.main_page.goto_product()
        self.main_page.click_username_button()
        self.main_page.click_quit_button()
        return LoginPage(self.main_page.driver)  # 退出操作返回主页面

    #登录-获取当前登录的用户-退出
    def get_username_quit(self):
        self.main_page.get_username()
        self.main_page.click_username_button()
        self.main_page.click_quit_button()
        return LoginPage(self.main_page.driver)  # 退出操作返回主页面

quit_action.py

from element_info.main.main_page import MainPage
from element_info.login.login_page import LoginPage


class QuitAction():
    def __init__(self, driver):
        self.main_page = MainPage(driver)

    def quit_action(self):
        self.main_page.click_username_button()
        self.main_page.click_quit_button()
        return LoginPage(self.main_page.driver)

create_bug_action.py

# -- coding: utf-8 --
# @Time : 2022/7/22 17:54
# @Author : siyu.yang
# @File : create_bug_action.py
# 可以把操作封装成功能函数也行
from element_info.qa.create_bug_page import CreateBUGPage
from actions.main_action import MainPage
import os


class CreateBugAction():
    def __init__(self, driver):
        self.bug_page = CreateBUGPage(driver)

    def commit_bug_action(self, deadline, bug_title, report_steps_content):
        '''
        提bug
        :param deadline: 截至日期
        :param bug_title: bug的标题
        :param report_steps_content: 重现步骤
        :return:
        '''
        self.bug_page.click_bug_module_link()
        self.bug_page.click_commit_bug_link()
        self.bug_page.click_product_selects()
        self.bug_page.click_Ecommerce_projects_select()
        self.bug_page.wait(1)
        self.bug_page.click_module_selects()
        self.bug_page.click_customer_center_select()
        self.bug_page.wait(1)
        self.bug_page.click_bug_selects()
        self.bug_page.click_page_error_select()
        self.bug_page.click_bug_os_selects()
        self.bug_page.click_bug_windows_select()
        self.bug_page.click_bug_browser_selects()
        self.bug_page.click_chrome_select()
        self.bug_page.click_edition_selects()
        self.bug_page.click_v1_0_select()
        self.bug_page.wait(1)
        self.bug_page.click_loadAllUsers()
        self.bug_page.wait(1)
        self.bug_page.click_assignedTo_selects()
        self.bug_page.wait(1)
        self.bug_page.click_LanShu_select()
        self.bug_page.wait(1)
        self.bug_page.input_deadline(deadline)
        self.bug_page.input_bug_title(bug_title)
        self.bug_page.click_severity_selects()
        self.bug_page.click_two_severity_select()
        self.bug_page.click_pri_selects()
        self.bug_page.click_two_pri_select()
        self.bug_page.enter_report_steps_iframe()
        self.bug_page.clear_report_steps_body()
        self.bug_page.input_report_steps_body(report_steps_content)
        self.bug_page.switch_to_frame()
        self.bug_page.switch_to_default_content()
        self.bug_page.scroll(150)
        self.bug_page.click_storyBox_span()
        self.bug_page.click_upload_file_button()
        os.system('E:/auto3_script/up.exe')
        self.bug_page.scroll(100)
        self.bug_page.click_submit_button()
        return MainPage(self.bug_page.driver)

testcase层,目录结果如下图:

login_case.py

# -- coding: utf-8 --
# @Time : 2022/7/22 17:57
# @Author : siyu.yang
# @File : login_case.py
# @Software: PyCharm
import unittest
from actions.login_action import LoginAction
from common.selenium_base_case import SeleniumBaseCase
from common.test_data_utils import TestDataUtils


class LoginCase(SeleniumBaseCase):
    test_class_data = TestDataUtils('login_suit', 'login_case', 'LoginCase').convert_exceldata_to_testdata()

    def setUp(self):
        super().setUp()
        print('LoginCase 测试类初始化父类')
        # self.test_class_data = TestDataUtils('login_suit', 'LoginCase').convert_exceldata_to_testdata()
        #

    @unittest.skipIf(test_class_data['test_login_success'].get('isnot'), reason='如果条件为真,就执行改用例')
    def test_login_success(self):
        test_function_data = self.test_class_data['test_login_success']
        login_action = LoginAction(self.base_page.driver)
        login_action = login_action.login_success(test_function_data['test_parameter'].get('username'),
                                                  test_function_data['test_parameter'].get('password'))
        actual = login_action.get_username()
        self.assertEqual(actual, test_function_data['expected_result'], 'test01,登录失败')

    @unittest.skipIf(test_class_data['test_login_fail_case'].get('isnot'), reason='如果条件为真,就执行改用例')
    def test_login_fail(self):
        test_function_data = self.test_class_data['test_login_fail_case']
        login_action = LoginAction(self.base_page.driver)
        actual = login_action.login_fail(test_function_data['test_parameter'].get('username'),
                                         test_function_data['test_parameter'].get('password'))
        self.assertEqual(actual, test_function_data['expected_result'])


if __name__ == '__main__':
    unittest.main()

quit_case.py

import unittest
from actions.login_action import LoginAction
from actions.quit_action import QuitAction
from common.selenium_base_case import SeleniumBaseCase
from common.test_data_utils import TestDataUtils


class QuitCase(SeleniumBaseCase):
    test_class_data = TestDataUtils('main_suit', 'quit_case', 'QuitCase').convert_exceldata_to_testdata()
    def setUp(self):
        super().setUp()
        print('LoginCase 测试类初始化父类')

    @unittest.skipIf(test_class_data['test_quit_success'].get('isnot'), reason='如果条件为真,就执行改用例')
    def test_quit_success(self):
        test_function_data = self.test_class_data['test_quit_success']
        """正常退出操作"""
        login_action = LoginAction(self.driver)
        main_page = login_action.default_login()
        quit_action = QuitAction(main_page.driver)
        login_page = quit_action.quit_action()  # 退出后返回主页面
        actual = login_page.get_title()
        self.assertEqual(actual.__contains__('用户登录'), True)


if __name__ == '__main__':
    unittest.main()

commit_bug_case.py

import unittest
from actions.login_action import LoginAction
from actions.create_bug_action import CreateBugAction
from common.selenium_base_case import SeleniumBaseCase
from common.test_data_utils import TestDataUtils


class CreateBugSuit(SeleniumBaseCase):
    test_bug_class_data = TestDataUtils('qa_suit', 'qa_case', 'CreateBugSuit').convert_exceldata_to_testdata()

    def setUp(self):
        super().setUp()
        print('LoginCase 测试类初始化父类')
        # self.test_bug_class_data = TestDataUtils('bug_suit', 'CreateBugSuit').convert_exceldata_to_testdata()

    @unittest.skipIf(test_bug_class_data['test_commit_bug_success'].get('isnot'), reason='如果条件为真,就执行改用例')
    def test_commit_bug_success(self):
        """正常提交bug操作"""
        test_function_data = self.test_bug_class_data['test_commit_bug_success']
        login_action = LoginAction(self.base_page.driver)
        main_page = login_action.default_login()
        main_page.goto_qa()

        bug_page = CreateBugAction(main_page.driver)
        bug_page.commit_bug_action(test_function_data['test_parameter'].get('deadline'),
                                   test_function_data['test_parameter'].get('bug_title'),
                                   test_function_data['test_parameter'].get('report_steps_content'))
        actual_result = main_page.get_title()
        self.assertEqual(actual_result.__contains__(test_function_data['expected_result']), True)


if __name__ == '__main__':
    unittest.main()

screen_shot 截图图片层

test_data测试数据层,目录结构如下图;


其中login_case.xlsx内容为,其它页面内容相似:

runner 运行层,run_all_case.py

import os, unittest
import shutil
import sys
sys.path.append('C:\\Users\\kcadmin\\Desktop\\python code\\PO_UI_Test_Framework')
from common import HTMLTestReportCN
from common.config_utils import config_obj
from common.email_utils import EmailUtils

current_path = os.path.dirname(__file__)
case_path = os.path.join(current_path, '..', config_obj.case_path)
report_path = os.path.join(current_path, '..', config_obj.report_path)


class RunAllCase:
    def __init__(self):
        self.test_case = case_path
        self.report_path = report_path
        self.title = "禅道UI自动化测试报告"
        self.description = "禅道UI自动化测试报告"

    def run(self):
        discover = unittest.defaultTestLoader.discover(start_dir=self.test_case,
                                                       pattern='*_case.py',
                                                       top_level_dir=self.test_case)
        all_suit = unittest.TestSuite()
        all_suit.addTest(discover)
        report_dir = HTMLTestReportCN.ReportDirectory(self.report_path)
        report_dir.create_dir(self.title)
        dir_path = HTMLTestReportCN.GlobalMsg.get_value('dir_path')
        report_path = HTMLTestReportCN.GlobalMsg.get_value('report_path')
        fp = open(report_path, 'wb')
        runner = HTMLTestReportCN.HTMLTestRunner(stream=fp,
                                                 title=self.title,
                                                 description=self.description,
                                                 tester='YangShiYu')
        runner.run(all_suit)
        fp.close()
        return dir_path


if __name__ == '__main__':
    dir_path = RunAllCase().run()  #运行所有用例
    #将生成的测试报告打包成zip文件,并发送邮件
    # shutil.copytree(dir_path,sys.argv[1])
    smtp_body = '禅道UI自动化测试报告,请注意查收!'
    send_email_obj = EmailUtils(smtp_body, dir_path).zip_send_mail()

最后运行run_all_case.py,会自动生产测试报告和产生日志文件,且对应的邮件也会收到运行的测试报告

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表