python seleniumunittest 及 ddt 数据驱动测试

Posted jianeng

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python seleniumunittest 及 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("打开页面失败 
%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 元素 
页面链接:%s 
%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 元素 
页面链接:%s 
%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("上传文件失败
 上传按钮定位: %s
 上传文件: %s
 " % (loc, file_path))
        except Exception as err:
            log.error("上传文件失败: %s %s
%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 
%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 
%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 
%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 
%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 
%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 
%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 
%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 
%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 
%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 
%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

 读取 excel 测试数据(使用 list_in_dict() 方法)

技术分享图片
#!/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 xlrd


class ExcelUtils(object):
    def __init__(self, excel_path, sheet_name):
        # 打开 excel 文件
        self.data = xlrd.open_workbook(excel_path)
        # 获取指定的 sheet
        self.sheet = self.data.sheet_by_name(sheet_name)
        # 获取第一行的值
        self.row = self.sheet.row_values(0)
        # 获取第一列的值
        self.col = self.sheet.col_values(0)
        # excel 表的行数
        self.rowNum = self.sheet.nrows
        # excel 表的列数
        self.colNum = self.sheet.ncols
        # 当前行号
        self.curRowNo = 1

    def has_next(self):
        """
        当行数为0或者读取的行数小于行号时, 返回 False
        :return: True or False type: bool
        """
        if self.rowNum == 0 or self.rowNum <= self.curRowNo:
            return False
        else:
            return True

    def list_in_dict(self):
        """
        生成包含字典的列表数据, 第二行数据作为键, 第三行及之后的数据作为值
        :return: data_list type: list
        """
        data_list = []
        row_val = self.sheet.row_values(1)
        self.curRowNo += 1
        while self.has_next():
            data_dict = {}
            col = self.sheet.row_values(self.curRowNo)
            skip = 1
            for x in range(self.colNum):
                if row_val[x] == "Skip" and col[x] == "Yes":
                    skip = 0
                data_dict.setdefault(row_val[x], col[x])
            if skip == 1:
                data_list.append(data_dict)
            self.curRowNo += 1
        return data_list


if __name__ == __main__:
    value = ExcelUtils("../data/testdata/gateway/ThreePay.xlsx", "PayPage").list_in_dict()
    print(value)
excel_utils.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 logging
import logging.config
from common.conf_utils import Project

# 日志配置文件
logging.config.fileConfig(Project().read_log())


class Log(object):
    def __init__(self):
        # 创建一个日志器 logger
        self.logger = logging.getLogger("AutoTest")

    def __console(self, level, message):

        if level == info:
            self.logger.info(message)
        elif level == debug:
            self.logger.debug(message)
        elif level == warning:
            self.logger.warning(message)
        elif level == error:
            self.logger.error(message, exc_info=True)

    def debug(self, message):
        self.__console(debug, message)

    def info(self, message):
        self.__console(info, message)

    def warning(self, message):
        self.__console(warning, message)

    def error(self, message):
        self.__console(error, message)


if __name__ == "__main__":
    log = Log()
    log.info("---测试开始----")
    log.info("操作步骤1,2,3")
    log.info("----测试结束----")
logger.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 xml.etree.ElementTree as Et


class XmlUtils(object):
    def __init__(self, xml_path):
        # 打开xml文档
        self.tree = Et.ElementTree(file=xml_path)
        # 获得根元素对象
        self.root_obj = self.tree.getroot()

    def get_tag(self):
        """
        获得根节点名称
        :return: type: str
        """
        return self.root_obj.tag

    def get_attribute(self):
        """
        获得根节点属性
        :return: type: str
        """
        return self.root_obj.attrib

    def get_attr_by_tag(self, sec_attr, sec_value, third_tag):
        """
        根据二级标签的 sec_name 遍历此标签下的标签名为 third_tag 的所有属性数据
        :meth sec_attr: 二级标签的属性名 type: str
        :meth sec_value: sec_attr 对应的属性值 type: str
        :meth third_tag: 三级标签的标签名 type: str
        :return: third_tag 对应的所有属性数据 type: dict
        """
        data_dict = {}
        # 遍历 root 的下一层
        for next_node in self.root_obj:
            # 遍历 third_tag 标签的所有数据
            if next_node.attrib[sec_attr] == sec_value:
                for sub_item in next_node.iter(third_tag):
                    data_dict.setdefault(sub_item.text, tuple(sub_item.attrib.values()))
        return data_dict


if __name__ == __main__:
    print(XmlUtils(../data/locator/gateway/PayPage.xml).get_attr_by_tag("pageName", webpay, locator))
xml_utils.py

3、数据文件

技术分享图片

项目中配置文件的路径

技术分享图片
[gateway]
# 测试链接
login = http://192.168.xx.xx:xxxx/Test/

[domain]
# 测试支付域名
put_pay = /web/test1
put_standard = /web/test2
put_ncgame = /web/test3
put_safe = /web/test4
put_inst = /web/test5
put_multiple = /web/test6
put_quick = /web/test7
put_ever = /web/test8
put_secure = /web/test9
put_web = /web/test10
put_virtual = /web/test11
put_webbr = /web/test12
put_starcor = /web/test13

[xml]
# 页面元素文件路径
home = /data/locator/home/Gateway.xml
pay = /data//locator/gateway/PayPage.xml
result = /data//locator/gateway/ResultPage.xml
threepay = /data/locator/gateway/ThreePay.xml
threequick = /data/locator/gateway/ThreeQuick.xml
halfpay = /data/locator/gateway/TwoHalfPay.xml
halfquick = /data/locator/gateway/TwoHalfQuick.xml
twopay = /data/locator/gateway/TwoPay.xml
twoquick = /data/locator/gateway/TwoQuick.xml
check = /data/locator/gateway/Check.xml

[xlsx]
# 测试数据文件路径
threepay = /data/testdata/gateway/ThreePay.xlsx
threequick = /data/testdata/gateway/ThreeQuick.xlsx
halfpay = /data/testdata/gateway/TwoHalfPay.xlsx
halfquick = /data/testdata/gateway/TwoHalfQuick.xlsx
twopay = /data/testdata/gateway/TwoPay.xlsx
twoquick = /data/testdata/gateway/TwoQuick.xlsx
check = /data/testdata/gateway/Check.xlsx

[addr]
thr = /PaymentGateway/test
thrcre = /PaymentGateway/test/create
thrpay = /PaymentGateway/test/directtest/quick
two = /PaymentGateway
twocre = /PaymentGateway/test/directtest/create
twopay = /PaymentGateway/test/directtest/quick
twh = /PaymentGateway/test/indirecttest
twhcre = /PaymentGateway/test/directtest/create
twhpay = /PaymentGateway/test/directtest/quick
check = /test/check/
track = /test/uploadTrackingNo
apply_ref = /test/applyRefund
query_ref = /test/queryRefund
gateway.conf

日志配置

技术分享图片
[loggers]
keys=root,AutoTest

[handlers]
keys=fileHandler

[formatters]
keys=form

[logger_root]
level=INFO
handlers=fileHandler

[logger_AutoTest]
level=INFO
handlers=fileHandler
qualname=AutoTest
propagate=0

[handler_fileHandler]
class=handlers.TimedRotatingFileHandler
args=("logs/catalina.log", d, 1, 0, utf-8)
level=INFO
formatter=form

[formatter_form]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt= %Y-%m-%d %I:%M:%S %p
logging.conf

元素定位

技术分享图片
<?xml version="1.0" encoding="UTF-8"?>

<map>
    <!-- 测试页面 -->
    <page pageName="paythree">
        <!--Locator lists -->
        <locator type="id" timeOut="5" value="suburl">ComboboxAddr</locator>
        <locator type="id" timeOut="5" value="account">AccountId</locator>
        <locator type="id" timeOut="5" value="terminal">TerminalId</locator>
        <locator type="id" timeOut="5" value="order_amount">OrderAmount</locator>
        <locator type="id" timeOut="5" value="btnAdd">MakePayment</locator>
    </page>
</map>
ThreePay.xml

测试数据

技术分享图片

4、日志

技术分享图片

技术分享图片
2018-04-09 08:59:57 AM - AutoTest - INFO - test case path: E:Oceanpayment	estcase
2018-04-09 08:59:58 AM - AutoTest - INFO - report path: E:Oceanpayment
eportTestResult.html
2018-04-09 08:59:58 AM - AutoTest - INFO - ----- 开始执行测试用例 -----
2018-04-09 08:59:58 AM - AutoTest - INFO - ----- 启动浏览器 -----
2018-04-09 09:00:04 AM - AutoTest - INFO - 登录-首页
2018-04-09 09:00:05 AM - AutoTest - INFO - 点击 测试 链接
2018-04-09 09:00:06 AM - AutoTest - INFO - ----- 页面标题: 测试 -----
2018-04-09 09:00:06 AM - AutoTest - INFO - 选择提交地址
2018-04-09 09:00:06 AM - AutoTest - INFO - 输入帐号ID
2018-04-09 09:00:06 AM - AutoTest - INFO - 输入终端ID
2018-04-09 09:00:06 AM - AutoTest - INFO - 点击 make payemnt
2018-04-09 09:00:07 AM - AutoTest - INFO - ----- 页面链接: http://xxx/PaymentGateway/test.html;jsessionid=K96jh4bW0tdXCRLkqYPwjSqQPh1MhLq4MppvBXgnlh8y71wGNl21!-1602834038 -----
2018-04-09 09:00:07 AM - AutoTest - INFO - 输入 Card Number
2018-04-09 09:00:07 AM - AutoTest - INFO - 选择年月
2018-04-09 09:00:07 AM - AutoTest - INFO - 输入 Secure Code
2018-04-09 09:00:07 AM - AutoTest - INFO - 点击 PAY NOW
2018-04-09 09:00:08 AM - AutoTest - INFO - 登录测试-首页
2018-04-09 09:00:08 AM - AutoTest - INFO - 点击 测试 链接
2018-04-09 09:00:08 AM - AutoTest - INFO - ----- 页面标题: 测试 -----
2018-04-09 09:00:08 AM - AutoTest - INFO - 选择提交地址
2018-04-09 09:00:08 AM - AutoTest - INFO - 输入帐号ID
2018-04-09 09:00:08 AM - AutoTest - INFO - 输入终端ID
2018-04-09 09:00:08 AM - AutoTest - INFO - 点击 make payemnt
catalina.log

5、页面操作

技术分享图片

技术分享图片
#!/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 datetime
from common import basepage
from common import xml_utils
from common.conf_utils import Gateway

# 当前年份的下一年
YEAR = str(datetime.datetime.now().year + 1)
# 当前月份
MONTH = 0{0}.format(datetime.datetime.now().month)
# 格式: 月/年 "0119"
MON_YEAR = MONTH + YEAR[2:]

# 读取 xml 文件
xml_obj = xml_utils.XmlUtils(Gateway().read_path("xml", "pay"))


class Pay(basepage.Action):
    """
    测试页面
    """
    def __init__(self, selenium_driver):
        super().__init__(selenium_driver)
        dict_val = xml_obj.get_attr_by_tag(pageName, paypage, locator)

        # ----- 定位器,通过元素属性定位元素对象 ----- #
        # CardNumber
        self.num = dict_val[CardNumber]
        # "月份" 下拉框
        self.mon = dict_val[ComboboxMonth]
        # "年份" 下拉框
        self.year = dict_val[ComboboxYear]
        # SecureCode
        self.code = dict_val[SecureCode]
        # "PAY_NOW" 按钮
        self.pay = dict_val[PayNow]

    # ----- 页面操作 ----- #
    def loc_frame(self):
        self.switch_frame("iframe", 1)

    def input_card_number(self, card_number):
        # 输入 CardNumber
        self.send_keys(self.num, card_number)

    def select_card_month(self):
        # 选择 "月份" 下拉框
        self.select_combobox(self.mon, MONTH)

    def select_card_year(self):
        # 选择 "年份" 下拉框
        self.select_combobox(self.year, YEAR)

    def input_secure_code(self, secure_code):
        # 输入 SecureCode
        self.send_keys(self.code, secure_code)

    def click_pay_now(self):
        # 点击 "PAY_NOW" 按钮
        self.click_button(self.pay)
pay_page.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 pageobj.gateway.pay_page as Pay
from common.logger import Log


class PayAction(object):
    def __init__(self, driver):
        self.driver = driver
        self.log = Log()

    def put_pay(self, test_data):
        # 提交支付
        pay_page = Pay.Pay(self.driver)
        self.log.info("----- 页面链接: %s -----" % self.driver.current_url)
        if /web/vpay in self.driver.current_url:
            # 切换 frame
            pay_page.loc_frame()
        num = test_data[CardNumber]
        self.log.info("输入 Card Number: {}".format(num))
        pay_page.input_card_number(num)
        self.log.info("选择年月")
        pay_page.select_card_month()
        pay_page.select_card_year()
        self.log.info("输入 Secure Code")
        pay_page.input_secure_code(test_data[SecureCode])
        self.log.info("点击 PAY NOW")
        pay_page.click_pay_now()
pay.py

6、测试用例

技术分享图片

技术分享图片
#!/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 re
import unittest
import ddt
from common.browser import get_driver
from common.excel_utils import ExcelUtils as Exc
from common.logger import Log
from common.conf_utils import Gateway
from action.gateway import pay as Pa
from action.gateway import threepay as Tp
from action.gateway import threequick as Tq
from action.gateway import twopay as Twp
from action.gateway import twoquick as Twq
from action.gateway import twohalfpay as Thp
from action.gateway import twohalfquick as Thq
from action.gateway import result as Res
from action.home.gateway import Home

# 日志
log = Log()


@ddt.ddt
class TestGateway(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        # 浏览器驱动
        cls.driver = get_driver()
        log.info("----- 启动浏览器 -----")
        # 浏览器最大化
        cls.driver.maximize_window()
        # 测试地址
        cls.addr = "http://xxx/test"
        # 接口类型
        cls.create = "3"
        cls.pay = "2"

    @classmethod
    def tearDownClass(cls):
        log.info("----- 关闭浏览器 -----")
        cls.driver.close()

    def setUp(self):
        # 获取支付域名列表数据
        self.payval = Gateway().read_domain("domain")

    def tearDown(self):
        pass

    @ddt.data(*Exc(Gateway().read_path("xlsx", "threepay"), "PayPage").list_in_dict())
    def test_threepay(self, test_data):
        # 进入 测试
        Home(self.driver).enter_three()
        # 提交 3方支付
        addr_thr = self.addr + Gateway().read_val("addr", "thr")
        Tp.Action(self.driver).put_three(addr_thr, test_data["终端ID"][:6], test_data["终端ID"])
        # 信用卡支付
        domain = re.findall(r"/w+/w+.", self.driver.current_url)[0].replace(".", "")
        for val in self.payval:
            for domain_conf in val[1].split(","):
                if domain == domain_conf.strip():
                    getattr(Pa.PayAction(self.driver), val[0])(test_data)
                    break
        # 断言:测试结果与预期结果对比
        self.assertEqual(test_data[payment_details], Res.Back(self.driver).back_datails())
        self.assertEqual(test_data[payment_status], Res.Back(self.driver).back_status())


if __name__ == "__main__":
    unittest.main()
test_gateway.py

7、测试报告

基于 common 公共方法的  HTMLTestRunner.py 生成测试报告

技术分享图片

 

 

以上是关于python seleniumunittest 及 ddt 数据驱动测试的主要内容,如果未能解决你的问题,请参考以下文章

python简介及python安装及开发工具的安装及设置

python优缺点分析及python种类,编码-课堂笔记及课后总结

mac设置python及pip环境变量及安装mysqlclient

安装 CentOs 系统 及 Python 及 Scrapy 框架

python入门及进阶学习记 lesson1

Python 基础知识及安装配置