python selenium、unittest 及 ddt 数据驱动测试

PO模型的概念和理解:

PO就是一个设计思想,将代码以页面为单位进行组织,针对这个页面上的所有信息、相关操作都放到一个类中,从而使具体的测试用例变成了简单的调用和验证操作。

优点:进行了拆分和分层

缺点:对于复杂的业务page层变了,case也需要去改动

 目录结构:

run_main.py 执行文件
common 公共方法
data 存放配置文件、元素定位文件、测试数据文件
logs 存放日志文件
report 测试报告
pageobj 页面元素
action 页面操作
testcase 测试用例

 1、执行文件

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date    : 2018-04-15 09:00:00
# @Author  : Canon
# @Link    : https://www.python.org
# @Version : 3.6.1

import os
import sys
import unittest
from common.HTMLTestRunner import HTMLTestRunner
from common.logger import Log

# 当前脚本所在文件真实路径
CUR_PATH = os.path.dirname(os.path.realpath(__file__))

# 日志
log = Log()


def add_case(case_name="testcase", rule="test*.py"):
    """
    第一步:加载所有的测试用例
    :meth case_name: 测试用例文件夹名称 type: str
    :meth rule: 匹配规则 type: str
    :return:
    """
    # 用例文件夹路径
    case_path = os.path.join(CUR_PATH, case_name)
    # 如果不存在这个case文件夹,就自动创建一个
    if not os.path.exists(case_path):
        os.mkdir(case_path)
    log.info("test case path: %s" % case_path)
    # 定义discover方法的参数
    discover = unittest.defaultTestLoader.discover(case_path,
                                                   pattern=rule,
                                                   top_level_dir=None)
    return discover


def run_case(all_case, report_name="report"):
    """
    第二步:执行所有的用例, 并把结果写入 HTML 测试报告
    :meth all_case: 所有测试用例 type: str
    :meth report_name: 测试报告文件夹名称 type: str
    :return:
    """
    # now = time.strftime("_%Y-%m-%d %H-%M-%S")
    now = ""
    # 测试报告文件夹
    report_dir = os.path.join(CUR_PATH, report_name)
    # 如果不存在这个report文件夹,就自动创建
    if not os.path.exists(report_dir):
        os.mkdir(report_dir)
    report_abspath = os.path.join(report_dir, "TestResult" + now + ".html")
    log.info("report path: %s" % report_abspath)
    fp = open(report_abspath, "wb")
    runner = HTMLTestRunner(stream=fp,
                            title='自动化测试报告,测试结果如下:',
                            description='用例执行情况:')
    # 调用 add_case 函数返回值
    log.info("----- 开始执行测试用例 -----")
    runner.run(all_case)
    log.info("----- 结束执行测试用例 -----")
    fp.close()


def main():
    case_dir = "testcase/gateway/check"
    # case_dir = "testcase"
    # # 测试项目
    # program = sys.argv[1]
    # if program == "gateway":
    #     case_dir = "testcase/gateway/pay"
    # elif program == "check":
    #     case_dir = "testcase/gateway/check"
    # 1、加载用例
    load_case = add_case(case_dir)
    # 2、执行用例
    run_case(load_case)


if __name__ == "__main__":
    main()
run_main.py

2、公共方法目录

页面基本操作
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date    : 2018-04-15 09:00:00
# @Author  : Canon
# @Link    : https://www.python.org
# @Version : 3.6.1

import win32gui
import win32con
import time
import platform
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support.select import Select
from selenium.webdriver.common.action_chains import ActionChains
from common.logger import Log

# 日志
log = Log()


class Action(object):
    """
    BasePage封装所有页面的公共方法
    """
    def __init__(self, selenium_driver):
        """
        初始化 driver
        :meth selenium_driver: 浏览器驱动
        """
        self.driver = selenium_driver

    def _open(self, url, page_title):
        """
        打开页面,校验页面链接是否加载正确
        :meth url: 链接地址 type: str
        :meth page_title: 页面标题 type: str
        :return:
        """
        # 使用get打开访问链接地址
        self.driver.get(url)
        try:
            # 使用assert进行校验,打开的链接地址是否与配置的地址一致, 调用on_page()方法
            assert self.on_page(page_title), "打开页面失败 {}".format(url)
        except Exception as err:
            log.error("打开页面失败 \n%s" % err)
            raise Exception("打开页面失败")

    def open(self, base_url, page_title):
        """
        定义 open 方法,调用 _open() 打开链接
        :return:
        """
        self._open(base_url, page_title)

    def on_page(self, page_title):
        """
        使用 current_url 获取当前窗口 url 地址,进行与配置地址作比较,返回比较结果(True or False)
        :meth page_title: 页面标题 type: str
        :return: type: bool
        """
        return page_title in self.driver.title

    def find_element(self, time_out=10, *loc):
        """
        重写元素定位方法(单个元素)
        :meth time_out: 超时时间 type: int
        :meth loc: 元素定位 type: str
        :return:
        """
        try:
            # 等待元素出现
            # WebDriverWait(self.driver, 10).until(self.driver.find_element(*loc).is_displayed())
            WebDriverWait(self.driver, time_out).until(lambda driver: driver.find_element(*loc).is_displayed())
            return self.driver.find_element(*loc)
        except Exception as err:
            log.error("%s 页面中未能找到 %s 元素 \n页面链接:%s \n%s" %
                      (self.driver.title, loc, self.driver.current_url, err))

    def find_elements(self, time_out=10, *loc):
        """
        重写元素定位方法(多个元素)
        :meth time_out: 超时时间 type: int
        :meth loc: 元素定位 type: str
        :return:
        """
        try:
            # 等待元素出现
            WebDriverWait(self.driver, time_out).until(lambda driver: driver.find_element(*loc).is_displayed())
            return self.driver.find_elements(*loc)
        except Exception as err:
            log.error("%s 页面中未能找到 %s 元素 \n页面链接:%s \n%s" %
                      (self.driver.title, loc, self.driver.current_url, err))

    def upload_file(self, loc, file_path):
        """
        上传文件
        :param loc: 元素定位 type: tuple
        :param file_path: 文件路径 type: str
        :return:
        """
        try:
            # 点击上传按钮
            self.click_button(loc)
            # 判断是哪个操作系统
            if platform.system() == "Windows":
                file_path = file_path.replace("/", "\\")
                # 主窗口(上传文件对话框), chrome 浏览器弹窗标题是"打开", firefox 浏览器弹窗标题是"文件上传"
                dialog = win32gui.FindWindow('#32770', '打开')
                # 重试次数
                num = 5
                for i in range(num):
                    # 判断是否获取到窗口句柄
                    if dialog == 0:
                        time.sleep(1)
                        dialog = win32gui.FindWindow('#32770', '打开')
                    else:
                        # 上面三句依次寻找对象,直到找到输入框 edit 对象的句柄
                        combobox_ex32 = win32gui.FindWindowEx(dialog, 0, 'ComboBoxEx32', None)
                        combobox = win32gui.FindWindowEx(combobox_ex32, 0, 'ComboBox', None)
                        edit = win32gui.FindWindowEx(combobox, 0, 'Edit', None)
                        # 确定按钮
                        button = win32gui.FindWindowEx(dialog, 0, 'Button', None)
                        # 在输入框中, 输入绝对路径
                        win32gui.SendMessage(edit, win32con.WM_SETTEXT, None, file_path)
                        # 点击打开按钮
                        win32gui.SendMessage(dialog, win32con.WM_COMMAND, 1, button)
                        break
                if dialog == 0:
                    log.error("上传文件失败\n 上传按钮定位: %s\n 上传文件: %s\n " % (loc, file_path))
        except Exception as err:
            log.error("上传文件失败: %s %s\n%s" % (loc, file_path, err))

    def switch_frame(self, tag_name, num):
        """
        切换 frame 标签
        :meth tag_name: 元素定位 type: str
        :meth num: 位置 type: int
        :return:
        """
        try:
            ele_list = self.driver.find_elements_by_tag_name(tag_name)
            self.driver.switch_to.frame(ele_list[num])
        except Exception as err:
            log.error("切换 frame 错误: %s \n%s" % (tag_name, err))

    def exec_script(self, src):
        """
        用于执行js脚本,返回执行结果
        :meth src: js 脚本 type: str
        :return:
        """
        try:
            self.driver.execute_script(src)
        except Exception as err:
            log.error("执行js脚本错误: %s \n%s" % (src, err))

    def send_keys(self, loc, value, *text_name):
        """
        重写 send_keys 方法
        :meth loc: 元素定位 type: tuple
        :meth value: 输入值 type: str
        :meth text_name: 文本框名称 type: tuple
        :return:
        """
        try:
            if loc[0] == 'js':
                self.exec_script(loc[2])
            else:
                # 点击文本框
                self.find_element(int(loc[1]), *(loc[0], loc[2].format(text_name))).click()
                # 清空文本框
                self.find_element(int(loc[1]), *(loc[0], loc[2].format(text_name))).clear()
                # 输入文本值
                self.find_element(int(loc[1]), *(loc[0], loc[2].format(text_name))).send_keys(value)
        except AttributeError as err:
            log.error("输入文本错误: %s \n%s" % (loc, err))

    def click_button(self, loc, *btn_name):
        """
        点击操作
        :meth loc: 元素定位 type: tuple
        :meth btn_name: 按钮名称 type: tuple
        :return:
        """
        try:
            if loc[0] == 'js':
                self.exec_script(loc[2])
            else:
                self.find_element(int(loc[1]), *(loc[0], loc[2].format(btn_name))).click()
        except AttributeError as err:
            log.error("点击元素错误: %s \n%s" % (loc, err))

    def select_checkbox(self, loc):
        """
        勾选复选框
        :meth loc: 元素定位 type: tuple
        :return:
        """
        try:
            if loc[0] == 'js':
                self.exec_script(loc[2])
            else:
                # 判断复选框是否被勾选
                if not self.find_element(int(loc[1]), *(loc[0], loc[2])).is_selected():
                    self.find_element(int(loc[1]), *(loc[0], loc[2])).click()
        except Exception as err:
            log.error("勾选文本框错误: %s \n%s" % (loc, err))

    def mouse_move(self, loc):
        """
        模拟鼠标悬停
        :meth loc: 元素定位 type: tuple
        :return:
        """
        try:
            # 鼠标移到悬停元素上
            ActionChains(self.driver).move_to_element(self.find_element(int(loc[1]), *(loc[0], loc[2]))).perform()
        except Exception as err:
            log.error("鼠标悬停错误: %s \n%s" % (loc, err))

    def select_combobox(self, loc, value):
        """
        选择下拉框的值, <select>标签下拉菜单
        :meth loc: 元素定位 type: tuple
        :meth value: 选项值 type: str
        :return:
        """
        try:
            if loc[0] == 'js':
                self.exec_script(loc[2])
            else:
                Select(self.find_element(int(loc[1]), *(loc[0], loc[2]))).select_by_value(value)
        except Exception as err:
            log.error("选择下拉框错误: %s \n%s" % (loc, err))

    def select_ul(self, select_loc, item_loc, select, item):
        """
        选择下拉框的值, 非<select>标签下拉菜单
        :meth select_loc: 下拉框元素定位 type: tuple
        :meth item_loc: 选项值元素定位 type: tuple
        :meth select: 下拉框名称 type: str
        :meth item: 选项值 type: str
        :return:
        """
        try:
            if item_loc[0] == 'js':
                self.exec_script(item_loc[2])
            elif select_loc[0] == 'js':
                self.exec_script(select_loc[2])
            else:
                loc_sele = (select_loc[0], int(select_loc[1]), select_loc[2].format(select))
                self.click_button(loc_sele)
                loc_val = (item_loc[0], int(item_loc[1]), select_loc[2].format(select, item))
                self.click_button(loc_val)
            # 拖动控件内的滚动条
            # self.driver.execute_script("document.getElementsByClassName('dropdown-menu inner')[1].scrollTop=500;")
        except Exception as err:
            log.error("选择下拉框错误: %s, %s \n%s" % (select_loc, item_loc, err))

    def get_attrval(self, loc, value):
        """
        获取文本框的值
        :meth loc: 元素定位 type: tuple
        :meth value: 文本框的属性名 type: str
        :return:
        """
        try:
            if loc[0] == 'js':
                return self.exec_script(loc[2])
            else:
                return self.find_element(int(loc[1]), *(loc[0], loc[2])).get_attribute(value)
        except Exception as err:
            log.error("获取文本框错误: %s \n%s" % (loc, err))

    def get_text(self, loc):
        """
        获取标签中的文本值
        :meth loc: 元素定位 type: tuple
        :return:
        """
        try:
            if loc[0] == 'js':
                return self.exec_script(loc[2])
            else:
                return self.find_element(int(loc[1]), *(loc[0], loc[2])).text
        except Exception as err:
            log.error("获取标签中的文本值错误: %s \n%s" % (loc, err))
basepage.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date    : 2018-04-15 09:00:00
# @Author  : Canon
# @Link    : https://www.python.org
# @Version : 3.6.1

from selenium import webdriver


def get_driver():
    """ 无界面运行 """
    # 配置浏览器参数
    # options = webdriver.ChromeOptions()
    # options.add_argument('--headless')
    # return webdriver.Chrome(chrome_options=options)
    """ 图形界面运行 """
    return webdriver.Chrome()
browser.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date    : 2018-04-15 09:00:00
# @Author  : Canon
# @Link    : https://www.python.org
# @Version : 3.6.1

import os
from configparser import ConfigParser

# 项目路径
CUR_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


class Project(object):
    def __init__(self):
        self.conf = ConfigParser()
        # 读取项目的配置文件
        self.conf.read(CUR_PATH + "/data/config/project.conf", encoding='UTF-8')

    def read_gateway(self):
        return CUR_PATH + self.conf.get("conf", "gateway")

    def read_oms(self):
        return CUR_PATH + self.conf.get("conf", "oms")

    def read_log(self):
        """
        读取日志的配置文件
        :return:
        """
        return CUR_PATH + self.conf.get("log", "path")


class Gateway(object):
    def __init__(self):
        self.conf = ConfigParser()
        # 读取支付网关的配置文件
        self.conf.read(Project().read_gateway(), encoding='UTF-8')

    def read_link(self):
        # 读取支付网关登录链接
        return self.conf.get("gateway", "login")

    def read_domain(self, section):
        """
        读取支付域名名称
        :meth section: 支付域名名称 type: str
        :return: 包含元组的列表
        """
        return self.conf.items(section)

    def read_path(self, sec, opt):
        return CUR_PATH + self.conf.get(sec, opt)

    def read_val(self, sec, opt):
        return self.conf.get(sec, opt)


if __name__ == '__main__':
    val = Gateway().read_domain("domain")
    print(val)
conf_utils.py

猜你喜欢

转载自www.cnblogs.com/jianeng/p/9339397.html