Python自动化测试系列[v1.0.0][高效自动化设计]

Python多线程应用于自动化测试

将多线程在测试巧妙地应用,确实会带来很多好处,并且这是充分利用机器资源执行高效率测试很好的方式

# -*- coding: utf-8 -*-
import threading
from time import ctime
import time
from selenium import webdriver


def test_search(browser, word):
    print("Start search at: %s\n" % ctime())
    print("Browser is: %s\n" % browser)
    if browser == 'ie':
        browser_driver = webdriver.Ie()
    elif browser == 'chrome':
        browser_driver = webdriver.Chrome()
    elif browser != 'ie' and browser != 'chrome':
        print("browser 参数非'ie 或 chrome'启动Fiefox")
        browser_driver = webdriver.Firefox()

    browser_driver.get("http://www.baidu.com")
    browser_driver.find_element_by_id("kw").send_keys(word)
    browser_driver.find_element_by_id("su").click()
    time.sleep(3)
    browser_driver.quit()


if __name__ == '__main__':
    dicts = {'chrome':'__davieyang__','ie':'davieyang','davieyang':'davieyang__'}
    threads = []
    for browser, word in dicts.items():
        t = threading.Thread(target = test_search, args=(browser, word))
        threads.append(t)
    for t in range(len(dicts)):
        threads[t].start()
    for t in range(len(dicts)):
        threads[t].join()
# -*- coding: utf-8 -*-
from selenium import webdriver
from time import sleep
from time import ctime
import multiprocessing


def start_browser(browser, time):
    if browser == "chrome":
        print("starting chrome browser now! %s" % ctime())  # 控制台打印当前时间 
        chrome_driver = webdriver.Chrome()
        chrome_driver.get("http://www.baidu.com")
        sleep(time)
        chrome_driver.quit()
    elif browser == "firefox":
        print("starting firefox browser now! %s" % ctime())  # 控制台打印当前时间
        fire_driver = webdriver.Firefox()
        fire_driver.get("http://www.baidu.com")
        sleep(time)
        fire_driver.quit()
    else: 
        print("starting ie browser now! %s" %ctime())  # 控制台打印当前时间
        ie_driver = webdriver.Ie()
        ie_driver.get("http://www.baidu.com")
        sleep(time)
        ie_driver.quit()


#  定义字典参数        
browser_dict = {"chrome": 3, "firefox": 4}
#  定义空List用于存储进程
start_browser_processing = []
#  循环字典Key-Value,创建进程并加入到List中
for browser, time in browser_dict.items():
    processing_browser = multiprocessing.Process(target = start_browser, args = (browser, time))
    start_browser_processing.append(processing_browser)

if __name__ == '__main__':
    for processing_browser in range(len(browser_dict)):
        start_browser_processing[processing_browser].start()
    for processing_browser in range(len(browser_dict)):
        start_browser_processing[processing_browser].join()
    print(u"全部结束 %s" % ctime())

PO设计模式I

当我们以脚本的形式编写了大量的自动化测试代码后,很容易发现代码可读性太差了、任何一个测试脚本几乎毫无扩展能力或者说扩展起来只能修改边边角角、一旦被测系统的UI层发生变化对于测试代码的修改来说简直就是灾难性的,整个自动化工程几乎毫无结构可言,当面临这些问题的时候实际上已经失去了一部分自动化测试原有的职能

任何自动化测试团队在开展自动化测试这项活动的之前,必须考虑的是这项活动的ROI,自动化测试的本职是解放人力,基于前边所述的常见情况不仅仅其ROI非常低,也违背了自动化的初衷

基于这个大背景下,常规的自动化测试活动必须引入PO模式,它是一种自动化测试的设计模式,也是一种解耦的思想即:以页面为单位,将页面中的控件及控件的动作逐一提炼,从而在测试脚本中可以直接使用,其最终目的是使得元素定位表达式``页面对象``测试代码三者分离

而分离后的测试代码,具有极高的可复用性、可读性,可维护性,可扩展性,而这像极了面向对象思想更像极了Java里Spring的MVC模式和Django的MTV模式

定义基本页面类

在自动化测试活动中,每个页面都有其独特的地方,每个月面有都与其共性,为了达到PO模式的目的,首先需要考虑的是每个月面的共性,第一项需要做的就是定义页面的基本特性及活动,它是所有页面类的基类,是独立于所有实际页面之外的

实例代码

# encoding = utf-8
from selenium import webdriver
from selenium.webdriver.common.by import By
from time import sleep


class Page():
    """
    基础类,仅用于定义一些页面常规内容及方法
    后续各个页面类继承该类实现各自页面的特别内容及方法
    """
    login_url = 'http://mail.163.com'  # 定义基类参数默认值,当调用时未提供该参数时使用

    def __init__(self, driver, base_url=login_url):
        self.base_url = base_url
        self.driver = driver
        self.timeout = 30

    def target_page(self):
        return self.driver.current_url == self.base_url  #  判断当前打开的url与参数给的url是否一致

    def open(self, url):
    	"""打开页面"""
        url = self.base_url
        self.driver.get(url)
        print(self.driver.current_url)
        
    def find_element(self, *loc):
        return self.driver.find_element(*loc)

定义具体页面类

定义了基本页面类,接下来便是具体到每个页面,因为每个也面都有不同,定义具体页面类也是定义其不同,但凡有相同的都应该抽象到基本页面类中去定义

class LoginPage(Page):
    """
    登陆页面,页面对象类
    """
    url = '/'
    """
    此处我们暂时称页面元素为对象,虽然本代码并非那么像对象,后续的框架中我们更高度的封装它便是个可操作的对象
    """
    username_loc = (By.NAME, "email")  # 页面控件对象:输入用户名的input控件
    password_loc = (By.NAME, "password")  # 页面控件对象:输入密码的input控件
    submit_loc = (By.ID, "dologin")  # 页面控件对象:登陆按钮的button控件

    """
    为每个页面元素对象封装其相对应的方法
    """
    def input_username(self, username):
        self.find_element(*self.username_loc).send_keys(username)  # 输入用户名

    def input_password(self, password):
        self.find_element(*self.password_loc).send_keys(password)  # 输入密码

    def click_submitbutton(self):
        self.find_element(*self.submit_loc).click()  # 点击登陆按钮


def user_login(driver, username, password):
    login_page = LoginPage(driver)
    login_page.open()
    sleep(5)
    driver.switch_to.frame(driver.find_element_by_xpath("//*[@id='loginDiv']/iframe"))
    login_page.input_username(username)
    sleep(3)
    login_page.input_password(password)
    sleep(3)
    login_page.click_submitbutton()

编写测试脚本

定义基本页面类是为了更快速的定义具体页面类,使得页面对象的共性可以复用,减少测试代码量;定义具体页面类实际上是扩展了基本页面类,相对于基本页面类而言它是独特的页面类;之后,便可以轻松的编写自动化测试脚本,脚本内容也变得简洁而清晰

def login_main163():
    try:
        driver = webdriver.Chrome()
        username = 'xxxxxx'  # 登陆邮箱需要的真实账号
        password = 'xxxxxx'  # 登陆邮箱需要的真实密码
        user_login(driver, username, password)  # 调用前面封装好的user_login方法
        sleep(3)  #  等待3秒
        driver.switch_to.default_content()  # 切换出iframe
        assert_string = driver.find_element_by_xpath("/html/body/div[1]/nav/div[1]/ul/li[1]/span[2]").text
        print(assert_string)
        assert (assert_string == '收 信')  # 断言关键字
    finally:
        driver.quit()  # 关闭浏览器窗口


if __name__ == '__main__':
    main()

PO设计模式II

任何自动化测试团队在开展自动化测试这项活动的之前,必须考虑的是这项活动的ROI,自动化测试的本职是解放人力,基于前边所述的常见情况不仅仅其ROI非常低,也违背了自动化的初衷

基于这个大背景下,常规的自动化测试活动必须引入PO模式,它是一种自动化测试的设计模式,也是一种解耦的思想即:以页面为单位,将页面中的控件及控件的动作逐一提炼,从而在测试脚本中可以直接使用,其最终目的是使得元素定位表达式 页面对象 测试代码三者分离

而分离后的测试代码,具有极高的可复用性、可读性,可维护性,可扩展性,而这像极了面向对象思想更像极了Java里Spring的MVC模式和Django的MTV模式

定义页面元素

自动化测试一切的根源都来自于页面元素,简单的说自动化测试就是让页面上的控件自己完成它的活动,自动化测试代码要控制这些控件,就首先要获取这些控件的位置,这里讲的不是元素定位的那些方式,而是应该如何处理这些元素定位表达式使其达到分离的效果

在自动化测试工程中新建一个诸如loginpage.ini的属性文件,当然这个文件最好是以页面为单位,每个页面独立分离,然后以控件名=定位方式>定位表达式的形式定义页面每个控件

[leadscloud_login]
input_user_name = xpath>//*[@id='main']/div/div[2]/div[2]/div[2]/div/form/div[1]/div/div/input
input_user_password = xpath>//*[@id='main']/div/div[2]/div[2]/div[2]/div/form/div[2]/div/div[1]/input
button_login = xpath>//*[@id='main']/div/div[2]/div[2]/div[2]/div/form/div[3]/div/button

公共类方法

定义公共类方法,用于解析前面的页面元素,获取元素定位,并返回页面元素对象

"""
用于解析配置文件,并获取页面元素定位表达式
"""
# -*- coding: utf-8 -*-
# @Time: 2019/2/26 11:43
# @Author : Yang DaWei
# @Project : LeadsCloudAutomation
# @FileName: ParseElementLocator.py
from configparser import ConfigParser
from time import sleep
from selenium.webdriver.support.wait import WebDriverWait


class ParseConfigFile:
    """
    初始化解析文件类
    """
    def __init__(self, driver, page_element_locator):
        self.driver = driver
        self.cf = ConfigParser()
        self.cf.read(page_element_locator, encoding='utf-8')

    def get_items_section(self, section_name):
        """
        获取配置文件中指定section下的所有option_name键值对,并以字典类型返回给调用者
        注意:使用self.cf.items(sectionName)此种方法获取到
        配置文件中的options内容均被转换成小写,如loginPage.frame将被转换成loginpage.frame
        """
        options_dict = dict(self.cf.items(section_name))
        return options_dict

    def get_option_value(self, section_name, option_name):
        """
        获取指定section下的指定option的值
        """
        value = self.cf.get(section_name, option_name)
        return value

    def get_element_location(self, section_name, option_name, timeout):
        """
        获取页面元素定位表达式,并以元素对象的形式返回给调用者
        :param section_name:
        :param option_name:
        :param timeout:
        :return:
        """
        driver = self.driver
        location = self.get_option_value(section_name, option_name)
        location_type = location.split(">")[0]
        location_value = location.split(">")[1]
        print("读取到的定位类型为:" + location_type + "\t读取到的定位信息为:" + location_value)
        try:
            element = WebDriverWait(driver, timeout).until(lambda x: x.find_element(by=location_type,
                                                                                    value=location_value))
            return element
        except Exception as e:
            print("定位元素超过" + str(timeout) + "秒,详细异常信息入下:")
            raise e

    def highlight_element(self, driver, element):
        """
        调用JS,用于高亮控件
        :param driver:
        :param element:
        :return:
        """
        driver.execute_script("arguments[0].setAttribute('style', arguments[1]);", element,
                              "background: yellow; border:2px solid red;")


if __name__ == '__main__':
    from selenium import webdriver
    # from Config import VarConfig
    from PageObject.Login_Page import Login_Page
    # locator_file = VarConfig.login_page

    driver = webdriver.Firefox()
    driver.get("http://test.xxxxxx.com/Front-Vue/#/login")
    sleep(5)
    Login_Page(driver).input_user_name().send_keys("yangdawei_10171")
    # ge = ParseConfigFile(driver, locator_file)
    # input_user_name = ge.get_element_location("leadscloud_login", "input_user_name", 5)
    # ge.highlight_element(driver, input_user_name)
    Login_Page(driver).input_user_password().send_keys("yangdawei_10166")
    # input_user_password = ge.get_element_location("leadscloud_login", "input_user_password", 5)
    # ge.highlight_element(driver, input_user_password)
    # input_user_password.send_keys("111111")
    # button_login = ge.get_element_location("leadscloud_login", "button_login", 5)
    # ge.highlight_element(driver, button_login)
    Login_Page(driver).login_button().click()
    driver.quit()

页面元素对象封装

通过公共类方法,读取页面元素定位信息,让后以页面控件为单位封装成可操作的对象

'''用于获取PageElementLocator.ini 中Login页面元素对象'''
# encoding = utf-8
"""
__title__ = ''
__author__ = 'davieyang'
__mtime__ = '2018/4/21'
"""
from Util.GetElements import *
from Util.ParseElementLocator import ParseConfigFile


class LoginPage(object):

    def __init__(self, driver):
        self.driver = driver
        self.parseCF = ParseConfigFile()
        self.loginOptions = self.parseCF.getItemsSection("163mail_login")
        print(self.loginOptions)

    def switchtoframe(self):
        try:
            # 从定位表达式配置文件中读取frame的定位表达式
            locatorExpression = self.loginOptions["loginPage.frame".lower()].split('>')[1]
            self.driver.switch_to.frame(locatorExpression)
        except Exception as e:
            raise e

    def switchtodefaultframe(self):
        try:
            self.driver.switch_to.default_content()
        except Exception as e:
            raise e

    def usernameobj(self):
        try:
            # 从定位表达式配置文件中读取定位用户名如输入框的定位方式和表达式
            locateType, locatorExpression = self.loginOptions["loginPage.username".lower()].split(">")
            # 获取登录页面的用户名输入框页面对象,返回给调用者
            elementobj = get_element(self.driver, locateType, locatorExpression)
            return elementobj
        except Exception as e:
            raise e

    def passwordobj(self):
        try:
            # 从定位表达式配置文件中读取定位密码输入框的定位方式和表达式
            locateType, locatorExpression = self.loginOptions["loginPage.password".lower()].split(">")
            # 获取登录页面的密码输入框页面对象,并返回给调用者
            elementoj = get_element(self.driver, locateType, locatorExpression)
            return elementoj
        except Exception as e:
            raise e

    def loginbutton(self):
        try:
            # 从定位表达式配置文件中读取定位登录按钮的定位方式和表达式
            locateType, locatorExpression = self.loginOptions["loginPage.loginbutton".lower()].split(">")
            # 获取登录页面的登录按钮页面对象,并返回给调用者
            elementobj = get_element(self.driver, locateType, locatorExpression)
            return elementobj
        except Exception as e:
            raise e


if __name__ == '__main__':
    from selenium import webdriver
    import time
    driver = webdriver.Chrome(executable_path="F:\\automation\\webdriver\\chromedriver.exe")
    driver.get("http://mail.163.com")
    time.sleep(5)
    login = LoginPage(driver)
    login.switchtoframe()
    login.usernameobj().send_keys("xxxx")
    login.passwordobj().send_keys("xxxx")
    login.loginbutton().click()
    time.sleep(10)
    login.switchtodefaultframe()
    assert u"未读邮件" in driver.page_source
    driver.quit()

简洁的测试脚本

# -*- coding: utf-8 -*-
# @Time: 2019/3/1 13:40
# @Author : Yang DaWei
# @Project : LeadsCloudAutomation
# @FileName: test_login_page.py
import time
import unittest
from selenium import webdriver
from PageObject.Login_Page import Login_Page

class Test_Login_Page(unittest.TestCase):

    driver = None
    @classmethod
    def setUpClass(cls):
        """ set Up method """
        cls.driver = webdriver.Chrome()
        cls.test_page = ''
        time.sleep(3)

    def tearDown(self):
        """ tear Down method """

    def setUp(self):
        """setUP pass"""

    @classmethod
    def tearDownClass(cls):
        """ tear Down method """
        cls.driver.close()

    def test_login(self):
        self.driver.get("http://admin.xxxxxxx.com/Front-Vue/#/")
        Login_Page(self.driver).input_user_name().send_keys("yangdawei_10171")
        Login_Page(self.driver).input_user_password().send_keys("111111")
        Login_Page(self.driver).login_button().click()
        try:
            self.assertTrue("仪表盘", self.driver.page_source)
            print("断言成功")
        except Exception as e:
            raise e
        time.sleep(10)

关键字驱动

在关键字驱动测试框架中,除了PO模式以及一些常规Action的封装外,一个很重要的内容就是读写EXCEL,在团队中如何让不会写代码的人也可以进行自动化测试? 我们可以将自动化测试用例按一定的规格写到EXCEL中去(如下图所示)
在这里插入图片描述
然后通过代码实现对具备这种规格的EXCEL进行解析,让你的代码获取EXCEL中的步骤,关键字,页面元素定位,操作方式,最后在写入执行结果,附上异常截图即可;团队中不会写代码的人居多,改改Excel执行也可以实现自动化测试

此处在初始化类的时候定义了两个颜色放进字典中,之后会当做参数传给写EXCEL的函数,当测试用例执行通过 用绿色字体标注pass,当执行失败的时候用红色字体标注failed

解析关键字

具体实现代码如下

# 用于实现读取Excel数据文件代码封装
# encoding = utf-8
"""
__project__ = 'KeyDri'
__author__ = 'davieyang'
__mtime__ = '2018/4/21'
"""
import openpyxl
from openpyxl.styles import Border, Side, Font
import time
 
 
class ParseExcel(object):
 
    def __init__(self):
        self.workBook = None
        self.excelFile = None
        self.font = Font(color=None)
        self.RGBDict = {'red': 'FFFF3030', 'green': 'FF008B00'}
 
    def loadWorkBook(self, excelPathAndName):
        # 将Excel加载到内存,并获取其workbook对象
        try:
            self.workBook = openpyxl.load_workbook(excelPathAndName)
        except Exception as e:
            raise e
        self.excelFile = excelPathAndName
        return self.workBook
 
    def getSheetByName(self, sheetName):
        # 根据sheet名获取该sheet对象
        try:
            # sheet = self.workBook.get_sheet_by_name(sheetName)
            sheet = self.workBook[sheetName]
            return sheet
        except Exception as e:
            raise e
 
    def getSheetByIndex(self, sheetIndex):
        # 根据sheet的索引号获取该sheet对象
        try:
            # sheetname = self.workBook.get_sheet_names()[sheetIndex]
            sheetname = self.workBook.sheetnames[sheetIndex]
        except Exception as e:
            raise e
        # sheet = self.workBook.get_sheet_by_name(sheetname)
        sheet = self.workBook[sheetname]
        return sheet
 
    def getRowsNumber(self, sheet):
        # 获取sheet中有数据区域的结束行号
        return sheet.max_row
 
    def getColsNumber(self, sheet):
        # 获取sheet中有数据区域的结束列号
        return sheet.max_column
 
    def getStartRowNumber(self, sheet):
        # 获取sheet中有数据区域的开始的行号
        return sheet.min_row
 
    def getStartColNumber(self, sheet):
        # 获取sheet中有数据区域的开始的列号
        return sheet.min_column
 
    def getRow(self, sheet, rowNo):
        # 获取sheet中某一行,返回的是这一行所有数据内容组成的tuple
        # 下标从1开始,sheet.rows[1]表示第一行
        try:
            # return sheet.rows[rowNo - 1] 因为sheet.rows是生成器类型,不能使用索引
            # 转换成list之后再使用索引,list(sheet.rows)[2]这样就获取到第二行的tuple对象。
            return list(sheet.rows)[rowNo - 1]
        except Exception as e:
            raise e
 
    def getCol(self, sheet, colNo):
        # 获取sheet中某一列,返回的是这一列所有数据内容组成的tuple
        # 下标从1开始,sheet.columns[1]表示第一列
        try:
            return list(sheet.columns)[colNo - 1]
        except Exception as e:
            raise e
 
    def getCellOfValue(self, sheet, coordinate = None, rowNo = None, colNo = None):
        # 根据单元格所在的位置索引获取该单元格中的值,下标从1开始
        # sheet.cell(row = 1, column = 1).value,表示excel中的第一行第一列的值
        if coordinate is not None:
            try:
                return sheet.cell(coordinate=coordinate).value
            except Exception as e:
                raise e
        elif coordinate is None and rowNo is not None and colNo is not None:
            try:
                return sheet.cell(row=rowNo, column=colNo).value
            except Exception as e:
                raise e
        else:
            raise Exception("Insufficient Coordinates of cell!")
 
    def getCellOfObject(self, sheet, coordinate = None, rowNo = None, colNo = None):
        # 获取某个单元格对象,可以根据单元格所在的位置的数字索引,也可以直接根据Excel中单元格的编码及坐标
        # 如getCellOfObject(sheet, coordinate='A1) or getCellOfObject(sheet, rowNo = 1, colNo = 2)
 
        if coordinate is not None:
            try:
                return sheet.cell(coordinate=coordinate)
            except Exception as e:
                raise e
        elif coordinate is None and rowNo is not None and colNo is not None:
            try:
                return sheet.cell(row=rowNo, column=colNo)
            except Exception as e:
                raise e
        else:
            raise Exception("Insufficient Coordinates of cell!")
 
    def writeCell(self, sheet, content, coordinate = None, rowNo = None, colNo = None, style=None):
        # 根据单元格在Excel中的编码坐标或者数字索引坐标向单元格中写入数据,下标从1开始
        # 参数style表示字体的颜色的名字,如red,green
        if coordinate is not None:
            try:
                sheet.cell(coordinate=coordinate).value = content
                if style is not None:
                    sheet.cell(coordinate=coordinate).font = Font(color=self.RGBDict[style])
                self.workBook.save(self.excelFile)
            except Exception as e:
                raise e
        elif coordinate is None and rowNo is not None and colNo is not None:
            try:
                sheet.cell(row=rowNo, column=colNo).value = content
                if style is not None:
                    sheet.cell(row=rowNo, column=colNo).font = Font(color=self.RGBDict[style])
                self.workBook.save(self.excelFile)
            except Exception as e:
                raise e
        else:
            raise Exception("Insufficient Coordinates of cell!")
 
    def writeCellCurrentTime(self, sheet, coordinate = None, rowNo = None, colNo = None):
        # 写入当前时间,下标从1开始
        now = int(time.time())  # 显示为时间戳
        timeArray = time.localtime(now)
        currentTime = time.strftime("%Y-%m-%d %H:%M:%S", timeArray)
        if coordinate is not None:
            try:
                sheet.cell(coordinate=coordinate).value = currentTime
                self.workBook.save(self.excelFile)
            except Exception as e:
                raise e
        elif coordinate is None and rowNo is not None and colNo is not None:
            try:
                sheet.cell(row=rowNo, column=colNo).value = currentTime
                self.workBook.save(self.excelFile)
            except Exception as e:
                raise e
 
 
if __name__ == "__main__":
    from Configurations.VarConfig import dataFilePath163
    pe = ParseExcel()
    pe.loadWorkBook(dataFilePath163)
    print("通过名称获取sheet对象名字:")
    pe.getSheetByName(u"联系人")
    print("通过Index序号获取sheet对象的名字")
    pe.getSheetByIndex(0)
    sheet = pe.getSheetByIndex(0)
    print(type(sheet))
    print(pe.getRowsNumber(sheet))
    print(pe.getColsNumber(sheet))
    cols = pe.getCol(sheet, 1)
    for i in cols:
        print(i.value)
    # 获取第一行第一列单元格内容
    print(pe.getCellOfValue(sheet, rowNo=1, colNo=1))
    pe.writeCell(sheet, u'中国北京', rowNo=11, colNo=11, style='red')
    pe.writeCellCurrentTime(sheet, rowNo=10, colNo=11)

解析Excel实际上方法不一


# encoding = utf-8
"""
__title__ = ''
__author__ = 'davieyang'
__mtime__ = '2018/4/21'
"""
import xlrd


class ExcelUtil():
    def __init__(self, excelPath, sheetName):
        self.data = xlrd.open_workbook(excelPath)
        self.table = self.data.sheet_by_name(sheetName)
        # 获取第一行作为key值
        self.keys = self.table.row_values(0)
        # 获取总行数
        self.rowNum = self.table.nrows
        # 获取总列数
        self.colNum = self.table.ncols

    def dict_data(self):
        if self.rowNum <= 1:
            print("总行数小于1")
        else:
            r = []
            j = 1
            for i in range(self.rowNum -1):
                s = {}
                # 从第二行取对应values值
                values = self.table.row_values(j)
                for x in range(self.colNum):
                    s[self.keys[x]] = values[x]
                r.append(s)
                j += 1
            return r


if __name__ == '__main__':
    excelPath = u"F:\\seleniumWithPython\\TestData\\163邮箱联系人.xlsx"
    sheetName = u"163账号"
    data = ExcelUtil(excelPath, sheetName)
    print(data.dict_data())

# 打开exlce表格,参数是文件路径
# data = xlrd.open_workbook('test.xlsx')

# table = data.sheets()[0]           #  通过索引顺序获取
# table = data.sheet_by_index(0)     #  通过索引顺序获取
# table = data.sheet_by_name(u'Sheet1')  # 通过名称获取

# nrows = table.nrows  # 获取总行数
# ncols = table.ncols  # 获取总列数

# 获取一行或一列的值,参数是第几行
#  print table.row_values(0)  # 获取第一行值
#  print table.col_values(0)  # 获取第一列值

关键字驱动测试代码

# 用于编写具体的测试逻辑代码
# encoding = utf-8
"""
__title__ = ''
__author__ = 'davieyang'
__mtime__ = '2018/4/21'
"""
from Util.GetElements import *
from Util.KeyBoardOperations import Keyboardkeys
from Util.ClipboardUtil import Clipboard
from Util.SmartWaitElements import WaitUtil
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from KeyDrivenMode.Actions.PageAction import *
import time

from KeyDrivenMode.Actions.PageAction import *
from Util.ParseExcelFile import ParseExcel
from Configurations.VarConfig import *
import time
import traceback

excelObj = ParseExcel()
excelObj.loadWorkBook(dataFilePath126)


def writeTestResult(sheetObj, rowNo, colNo, testResult, errorInfo = None, picPath = None):
    # 测试通过结果信息为绿色,失败为红色
    colorDict = {"pass": "green", "fail": "red"}
    # 因为测试用例工作表和用例步骤工作表中都有测试执行时间和测试结果列,定义此字典对象是为了区分具体应该写哪个工作表

    colDict = {"testCase": [testCase_runTime, testCase_testResult], "caseStep": [testStep_runTime, testStep_testResult]}
    try:
        # 在测试步骤工作表中,写入测试时间
        excelObj.writeCellCurrentTime(sheetObj, rowNo=rowNo, colNo=colDict[colNo][0])
        # 在测试步骤工作表中,写入测试结果
        excelObj.writeCell(sheetObj, content=testResult, rowNo=rowNo, colNo=colDict[colNo][1])
        if errorInfo and picPath:
            # 在测试步骤工作表中,写入异常信息
            excelObj.writeCell(sheetObj, content=errorInfo, rowNo=rowNo, colNo=testStep_errorInfo)
            # 在测试步骤工作表中,写入异常截图路径
            excelObj.writeCell(sheetObj, content=picPath, rowNo=rowNo, colNo=testStep_errorPic)
        else:
            # 在测试步骤工作表中,清空异常信息单元格
            excelObj.writeCell(sheetObj, content="", rowNo=rowNo, colNo=testStep_errorInfo)
            # 在测试步骤工作表中,清空异常截图路径单元格
            excelObj.writeCell(sheetObj, content="", rowNo=rowNo, colNo=testStep_errorPic)
    except Exception as e:
        print(u"写excel出错", traceback.print_exc())


def TestSendMailWithAttachment():
    try:
        # 根据Excel文件中的sheet名获取sheet对象
        caseSheet = excelObj.getSheetByName(u"测试用例")
        # 获取测试用例sheet中是否执行列对象
        isExeuteColumn = excelObj.getCol(caseSheet, testCase_isExecute)
        # 记录执行成功的测试用例个数
        successfulCase = 0
        # 记录需要执行的用力个数
        requiredCase = 0
        for idx, i in enumerate(isExeuteColumn[1:]):
            # 因为用例sheet中的第一行为标题行,无需执行
            # print(i.value)
            # 循环遍历“测试用例”表中的测试用例,执行被设置为执行的用例
            if i.value.lower() == "y":
                requiredCase += 1
                # 获取“测试用例”表中第idx+2行的数据
                caseRow = excelObj.getRow(caseSheet, idx +2)
                # 获取idx+2行的“步骤sheet”单元格内容
                caseStepSheetName = caseRow[testCase_testStepSheetName - 1].value
                # print(caseStepSheetName)

                # 根据用例步骤名获取步骤sheet对象
                stepSheet = excelObj.getSheetByName(caseStepSheetName)
                # 获取步骤sheet中步骤数
                stepNum = excelObj.getRowsNumber(stepSheet)
                # print(stepNum)
                # 记录测试用例i的步骤成功数
                successfulSteps = 0
                print(u"开始执行用例%s" % list(caseRow)[testCase_testCaseName - 1].value)
                for step in range(2, stepNum + 1):
                    # 因为步骤sheet中的第一行为标题行,无需执行
                    # 获取步骤sheet中的第step行对象
                    stepRow = excelObj.getRow(stepSheet, step)
                    # 获取关键字作为调用的函数名
                    keyWord = stepRow[testStep_keyWords - 1].value
                    # 获取操作元素定位方式作为调用的函数的参数
                    locationType = stepRow[testStep_locationType - 1].value
                    # 获取操作元素定位表达式作为调用函数的参数
                    locatorExpression = stepRow[testStep_locatorExpression - 1].value
                    # 获取操作值作为调用函数的参数
                    operateValue = stepRow[testStep_operateValue - 1].value
                    # 将操作值为数字类型的数据转换成字符串类型,方便字符串拼接
                    if isinstance(operateValue, int):
                        operateValue = str(operateValue)
                    print(keyWord, locationType, locatorExpression, operateValue)
                    expressionStr = ""
                    # 构造需要执行的python语句
                    # 对应的是PageAction.py文件中的页面动作函数调用的字符串表示
                    if keyWord and operateValue and locationType is None and locatorExpression is None:
                        expressionStr = keyWord.strip() + "(u'" +operateValue+"')"
                    elif keyWord and operateValue is None and locationType is None and locatorExpression is None:
                        expressionStr = keyWord.strip() + str()
                    elif keyWord and locationType and operateValue and locatorExpression is None:
                        expressionStr = keyWord.strip() + "('" + locationType.strip() + "', u'" + operateValue + "')"
                    elif keyWord and locationType and locatorExpression and operateValue:
                        expressionStr = keyWord.strip() + "('" + locationType.strip() + "', '" + locatorExpression.replace("'", '"').strip() + "', u'" + operateValue + "')"
                    elif keyWord and locationType and locatorExpression and operateValue is None:
                        expressionStr = keyWord.strip() + "('" + locationType.strip() + "', '" + locatorExpression.replace("'", '"').strip() + "')"
                    print(expressionStr)
                    try:
                        # 通过eval函数,将拼接的页面动作函数调用的字符串表示
                        # 当成有效的Python表达式执行,从而执行测试步骤的sheet中
                        # 关键字在PageAction.py文件中对应的映射方法
                        # 来完成对页面元素的操作
                        eval(expressionStr)
                        # 在测试执行时间列写入执行时间
                        excelObj.writeCellCurrentTime(stepSheet, rowNo=step, colNo=testStep_runTime)
                    except Exception as e:
                        # 截取异常屏幕图片
                        capturePic = capture_screen()
                        # 获取详细的异常堆栈信息
                        errorInfo = traceback.format_exc()
                        # 在测试步骤sheet中写入失败信息
                        writeTestResult(stepSheet, step, "caseStep", "faild", errorInfo, capturePic)
                        print(u"步骤 %s 执行失败!" % list(stepRow)[testStep_testStepDescribe - 1].value)
                    else:
                        # 在测试步骤sheet中写入成功信息
                        writeTestResult(stepSheet, step, "caseStep", "pass")
                        # 每成功一步successfulSteps变量自增1
                        successfulSteps += 1
                        print(u"步骤 %s 执行成功!" % list(stepRow)[testStep_testStepDescribe - 1].value)
                if successfulSteps == stepNum - 1:
                    # 当测试用例步骤sheet中所有的步骤都执行成功
                    # 才认为此测试用例执行通过,然后将成功信息写入
                    # 测试用例工作表中,否则写入失败信息
                    writeTestResult(caseSheet, idx + 2, "testCase", "pass")
                    successfulCase += 1
                else:
                    writeTestResult(caseSheet, idx+2, "testCase", "faild")
        print(u"共%d条用例,%d条需要被执行,本次直行通过%d条." % (len(isExeuteColumn)-1, requiredCase, successfulCase))
    except Exception as e:
        # 打印详细的异常堆栈信息
        print(traceback.print_exc())


if __name__ == "__main__":
    TestSendMailWithAttachment()
    """
    不借用PageAction.py
    # 创建Chrome浏览器实例
    driverchrome = webdriver.Chrome(executable_path=r"F:\automation\webdriver\chromedriver.exe")
    driverchrome.maximize_window()
    driverchrome.get("http://mail.126.com")
    time.sleep(5)
    assert u"126网易免费邮--你的专业电子邮局" in driverchrome.title
    print("access 126 successfully")

    wait = WaitUtil(driverchrome)
    wait.frame_to_be_available_and_switch_to_it("id", "x-URS-iframe")
    username = get_element(driverchrome, "xpath", "//input[@name='email']")
    username.send_keys("davieyang008")
    password = get_element(driverchrome, "xpath", "//input[@name='password']")
    password.send_keys("Ethan005x")
    password.send_keys(Keys.ENTER)
    time.sleep(5)
    assert u"网易邮箱" in driverchrome.title
    print("login successfully")

    element = wait.visibility_element_located("xpath", "//li[@id='_mail_component_70_70']")
    element.click()
    receiver = get_element(driverchrome, "xpath", "//div[contains(@id, '_mail_emailinput')]/input")
    receiver.send_keys("15901281916@163.com")
    subject = get_element(driverchrome, "xpath", "//div[@aria-label = '邮件主题输入框,请输入邮件主题']/input")
    subject.send_keys("new email")
    # 设置剪切板内容
    Clipboard.setText(u"F:\\a.txt")
    # 获取剪切板内容
    Clipboard.getText()
    attachment = get_element(driverchrome, "xpath", "//div[contains(@title, '点击添加附件')]")
    # 单击上传附件链接
    attachment.click()
    time.sleep(3)
    # 在上传附件windows弹窗中黏贴剪切板中的内容
    Keyboardkeys.twoKey('ctrl', 'v')
    # 模拟回车以便加载要上传的附件
    time.sleep(3)
    Keyboardkeys.oneKey('enter')
    # 切换进邮件正文的frame
    wait.frame_to_be_available_and_switch_to_it("xpath", "//iframe[@tabindex=1]")
    body = get_element(driverchrome, "xpath", "/html/body")
    # 输入邮件正文
    body.send_keys("abcabc")
    # 切出邮件正文的frame
    driverchrome.switch_to.default_content()
    get_element(driverchrome, "xpath", "//header//span[text()='发送']").click()
    time.sleep(3)
    assert u"发送成功" in driverchrome.page_source
    print(u"邮件发送成功")
    driverchrome.quit()
    :return:
    """
    """
    借用PageAction.py
    open_browser("chrome")
    maximize_browser()
    visit_url("http://mail.126.com")
    sleep(5)
    assert_string_in_pagesource(u"126网易免费邮--你的专业电子邮局")
    waitFrameToBeAvailableAndSwitchToIt("id", "x-URS-iframe")
    input_string("xpath", "//input[@name='email']", "davieyang008")
    input_string("xpath", "//input[@name='password']", "Ethan005x")
    click("id", "dologin")
    sleep(5)
    assert_title(u"网易邮箱")
    waitVisibilityOfElementLocated("xpath", "//li[@id='_mail_component_70_70']")
    click("xpath", "//li[@id='_mail_component_70_70']")
    input_string("xpath", "//div[contains(@id, '_mail_emailinput')]/input", "15901281916@163.com")
    input_string("xpath", "//div[@aria-label = '邮件主题输入框,请输入邮件主题']/input", "new email")
    click("xpath", "//div[contains(@title, '点击添加附件')]")
    sleep(3)
    paste_string(u"F:\\a.txt")
    press_enter_key()
    waitFrameToBeAvailableAndSwitchToIt("xpath", "//iframe[@tabindex=1]")
    input_string("xpath", "/html/body", "abcdef")
    switch_to_default_content()
    click("xpath", "//header//span[text()='发送']")
    time.sleep(3)
    assert_string_in_pagesource(u"发送成功")
    close_browser()
    """





本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/783949.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【c语言】玩转文件操作

&#x1f31f;&#x1f31f;作者主页&#xff1a;ephemerals__ &#x1f31f;&#x1f31f;所属专栏&#xff1a;C语言 目录 引言 一、文件的打开和关闭 1.流 2.标准流 3.文本文件和二进制文件 4.控制文件打开与关闭的函数 二、文件的顺序读写 三、文件的随机读写 1…

7月学术会议:7月可投的EI国际会议

随着科技的迅猛发展&#xff0c;学术交流与研讨成为了推动科研进步的重要途径。进入7月&#xff0c;众多高质量的EI国际会议纷纷拉开帷幕&#xff0c;为全球的科研工作者提供了一个展示研究成果、交流学术思想的平台。以下&#xff0c;我们将详细介绍一些在7月可投的EI国际会议…

Java集合升序降序、转Set的方法

Collections.sort(list,Comparator.comparing(OcApplySquareVo::getApplyName).reversed()); 集合转set /** 集合转set */Set<String> pkCodeSet rows.stream().map(RailwayWeighBookResult.RailwayWeighBook::getPkCode).collect(Collectors.toSet());

猫咪浮毛太多怎么处理?6年铲屎官最值得买的猫毛空气净化器分享

作为一位拥有6年铲屎经验的铲屎官&#xff0c;家中既有宝宝又有毛孩子的铲屎官家庭来说&#xff0c;空气中的宠物异味和猫毛不仅影响生活质量&#xff0c;更关乎家人的健康。普通空气净化器虽然能够提供基本的空气净化&#xff0c;但对于养猫家庭的特定需求&#xff0c;如去除宠…

Pytest单元测试系列[v1.0.0][Pytest基础]

Pytest安装与配置 和Unittest一样&#xff0c;Pytest是另一个Python语言的单元测试框架&#xff0c;与Unittest相比它的测试用例更加容易编写、运行方式更加灵活、报错信息更加清晰、断言写法更简洁并且它可以运行有unittest和nose编写的测试用例。 Pytest 安装 启动命令行&…

A股本周在3000点以下继续筑底,本周依然继续探底?

夜已深&#xff0c;市场传来了3个浓烈的消息&#xff0c;炸锅了&#xff0c;恐有大事发生&#xff0c;马上告诉所有人&#xff1a; 消息面&#xff1a; 1、中国经济周刊首席评论员钮文新称&#xff1a;不要等中小投资者都彻底希望&#xff0c;销户离场了&#xff0c;才发现该…

新恒汇过会一年多注册仍遥遥无期,实控人大额负债入股资金靠借款

《港湾商业观察》施子夫 自2022年6月递表深交所创业板获受理&#xff0c;新恒汇电子股份有限公司 &#xff08;以下简称&#xff0c;新恒汇&#xff09;的上市之路无疑颇显诸多坎坷。2022年7月&#xff0c;深交所下发第一轮审核问询函&#xff1b;同年11月&#xff0c;深交所下…

Bugly的底层是怎么实现的

Bugly 入门 首先&#xff0c;简要介绍什么是 Bugly 以及它的主要功能&#xff1a; Bugly 是什么&#xff1a; Bugly 是腾讯提供的一款移动应用质量监控工具&#xff0c;主要用于捕捉应用的崩溃、ANR&#xff08;应用无响应&#xff09;、卡顿和错误日志。 主要功能&#xff1…

微型导轨如何提升数控机床的稳定性?

数控机床是加工设备中常用的机床&#xff0c;精度和稳定性是衡量数控机床性能的重要指标。而微型导轨作为数控机床中重要的传动元件&#xff0c;数控机床与其具体结构性能是密不可分的&#xff0c;那么微型导轨如何提高数控机床的稳定性呢&#xff1f; 1、微型导轨通过采用先进…

【见刊通知】MVIPIT 2023机器视觉、图像处理与影像技术国际会议

MVIPIT 2023&#xff1a;https://ieeexplore.ieee.org/xpl/conhome/10578343/proceeding 入库Ei数据库需等20-50天左右 第二届会议征稿启动&#xff08;MVIPIT 2024&#xff09; The 2nd International Conference on Machine Vision, Image Processing & Imaging Techn…

暑假提升(3)[平衡二叉树之二--红黑树]

命为志存。 —— 朱熹 红黑树RBTree 1、诞生原因2、红黑树的概念3、红黑树的性质4、红黑树的设计4、1、节点设计4、2、插入操作的设计 5、总结 1、诞生原因 由于二叉树的局限性&#xff0c;进一步出现平衡二叉树&#xff0c;来帮助我们来进一步提升我们对数据的处理&#xff0…

【LabVIEW学习篇 - 1】:初始LabVIEW

文章目录 初始LabView前面板和程序框图前面板&#xff08;Front Panel&#xff09;程序框图&#xff08;Block Diagram&#xff09;交互和工作流程 练手小案例&#xff1a;LabView中实现加法操作 初始LabView LabVIEW&#xff08;Laboratory Virtual Instrument Engineering W…

数据要素资产化路径

一、数据治理&#xff1a;包括数据规范管理、数据治理管理、元数据管理、数据架构管理。 二、数据资产运营&#xff1a;包括数据目录视图、数据全生命周期、数据资产估值、数据资产定价、数据交易流通。 方向1&#xff1a;产业数字化&#xff08;难度系数&#xff1a;*&#…

出现d3dcompiler_43.dll缺失我们要怎么修复?教你科学修复d3dcompiler_43.dll

出现d3dcompiler_43.dll缺失其实也算是一种比较常见的dll文件丢失&#xff0c;毕竟现在很多在使用电脑的时候&#xff0c;都会胡乱的下载东西&#xff0c;然后导致电脑中毒&#xff0c;感染到d3dcompiler_43.dll文件&#xff0c;而导致d3dcompiler_43.dll文件被损坏&#xff0c…

docker安装oracle 11g

最近把一些常用数据库都移到docker了&#xff0c;而且是windows下&#xff0c;很是方便。偶尔还是要用一下Oracle&#xff0c;今天就试一下安装oracle 11g 在docker上。 一、搜索并拉取镜像 docker search oracle_11gdocker pull iatebes/oracle_11g二、运行容器和测试连接 …

微信小程序开发-003-首页(轮播图,状态栏,导航栏)

哈喽小伙伴们大家好,我是程序媛小李,今天,我们继续来开发微信小程序. 在这里,先贴上首页的效果图: 整个页面大概可以分为顶部的状态栏区域,轮播图区域,公司信息区域,商品导航区域,商品推荐区域,以及最下面的导航栏区域. 一,底部导航栏 在这里,我们遵循从外到内的原则,我们先来…

小白·使用Tesseract-OCR工具读取图片

1、直接pip安装 工具使用vscode和pycharm都可以。 这里介绍使用vscode的方法。 (1)、调出终端 (2)、安装依赖 (3)、编写代码 import pyocr import pyocr.builders from PIL import Image import re# 获取Tesseract-OCR工具 tools pyocr.get_available_tools() tool tools[…

数据融合工具(3)国家基本比例尺地形图分幅计算

情景再现&#xff0c;呼叫小编 数据获取和使用过程中&#xff0c;经常听到一个名词“分幅图幅号”…… 你的数据是按多大比例尺分幅的&#xff1f;我不知道&#xff0c;就一些字母和数值。 你把G47E018018范围内的数据裁剪提供&#xff0c;这个范围是啥&#xff1f; 你把镶嵌…

Android14之获取包名/类名/服务名(二百二十三)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

常见的自动化工具开发必备的源代码!

随着科技的飞速发展&#xff0c;自动化工具已经成为我们日常工作中不可或缺的一部分&#xff0c;自动化工具不仅极大地提高了工作效率&#xff0c;还降低了人为错误的可能性。 然而&#xff0c;要想开发出高效、稳定的自动化工具&#xff0c;掌握一些常见的源代码技巧是至关重…